From 8a7edceecd0de00ad80f92302feccb512353e3e8 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Fri, 18 Mar 2022 13:57:29 +0300 Subject: [PATCH 001/106] Temporary commit --- cvat-core/src/annotations-collection.js | 34 ++- cvat-core/src/annotations-objects.js | 162 ++++++++++++++- cvat-core/src/annotations.js | 263 ++++++++++++------------ cvat-core/src/enums.js | 4 + cvat-core/src/session.js | 8 +- 5 files changed, 330 insertions(+), 141 deletions(-) diff --git a/cvat-core/src/annotations-collection.js b/cvat-core/src/annotations-collection.js index 31892dc1fb0..0e6314cd560 100644 --- a/cvat-core/src/annotations-collection.js +++ b/cvat-core/src/annotations-collection.js @@ -18,6 +18,7 @@ CuboidTrack, Track, Shape, + Mask, Tag, objectStateFactory, } = require('./annotations-objects'); @@ -114,6 +115,7 @@ this.history = data.history; this.shapes = {}; // key is a frame this.tags = {}; // key is a frame + this.masks = {}; // key is a frame this.tracks = []; this.objects = {}; // key is a client id this.count = 0; @@ -135,6 +137,7 @@ tags: [], shapes: [], tracks: [], + masks: [], }; for (const tag of data.tags) { @@ -165,9 +168,20 @@ // In this case a corresponded message will be sent to the console if (trackModel) { this.tracks.push(trackModel); + result.tracks.push(trackModel); this.objects[clientID] = trackModel; + } + } - result.tracks.push(trackModel); + for (const mask of data.masks) { + const clientID = ++this.count; + const color = colors[clientID % colors.length]; + const maskModel = new Mask(mask, clientID, color, this.injection); + if (maskModel) { + this.masks[maskModel.frame] = this.masks[maskModel.frame] || []; + this.masks[maskModel.frame].push(maskModel); + this.objects[clientID] = maskModel; + result.masks.push(maskModel); } } @@ -178,19 +192,26 @@ const data = { tracks: this.tracks.filter((track) => !track.removed).map((track) => track.toJSON()), shapes: Object.values(this.shapes) - .reduce((accumulator, value) => { - accumulator.push(...value); + .reduce((accumulator, frameShapes) => { + accumulator.push(...frameShapes); return accumulator; }, []) .filter((shape) => !shape.removed) .map((shape) => shape.toJSON()), tags: Object.values(this.tags) - .reduce((accumulator, value) => { - accumulator.push(...value); + .reduce((accumulator, frameTags) => { + accumulator.push(...frameTags); return accumulator; }, []) .filter((tag) => !tag.removed) .map((tag) => tag.toJSON()), + masks: Object.values(this.masks) + .reduce((accumulator, frameMasks) => { + accumulator.push(...frameMasks); + return accumulator; + }, []) + .filter((mask) => !mask.removed) + .map((mask) => mask.toJSON()), }; return data; @@ -200,8 +221,9 @@ const { tracks } = this; const shapes = this.shapes[frame] || []; const tags = this.tags[frame] || []; + const masks = this.masks[frame] || []; - const objects = [].concat(tracks, shapes, tags); + const objects = [].concat(tracks, shapes, tags, masks); const visible = { models: [], data: [], diff --git a/cvat-core/src/annotations-objects.js b/cvat-core/src/annotations-objects.js index e4392810a33..b932dc95746 100644 --- a/cvat-core/src/annotations-objects.js +++ b/cvat-core/src/annotations-objects.js @@ -1,4 +1,4 @@ -// Copyright (C) 2021 Intel Corporation +// Copyright (C) 2021-2022 Intel Corporation // // SPDX-License-Identifier: MIT @@ -1311,6 +1311,165 @@ } } + class Mask extends Drawn { + constructor(data, clientID, color, injection) { + super(data, clientID, color, injection); + this.rle = data.rle; + this.shapeType = ObjectShape.MASK; + this.occluded = data.occluded; + this.zOrder = data.z_order; + } + + // Method is used to export data to the server + toJSON() { + return { + type: this.shapeType, + clientID: this.clientID, + occluded: this.occluded, + z_order: this.zOrder, + points: [...this.points], + rotation: this.rotation, + attributes: Object.keys(this.attributes).reduce((attributeAccumulator, attrId) => { + attributeAccumulator.push({ + spec_id: attrId, + value: this.attributes[attrId], + }); + + return attributeAccumulator; + }, []), + id: this.serverID, + frame: this.frame, + label_id: this.label.id, + group: this.group, + source: this.source, + }; + } + + // Method is used to construct ObjectState objects + get(frame) { + if (frame !== this.frame) { + throw new ScriptingError('Got frame is not equal to the frame of the shape'); + } + + return { + objectType: ObjectType.SHAPE, + shapeType: this.shapeType, + clientID: this.clientID, + serverID: this.serverID, + occluded: this.occluded, + lock: this.lock, + zOrder: this.zOrder, + points: [...this.points], + rotation: this.rotation, + attributes: { ...this.attributes }, + descriptions: [...this.descriptions], + label: this.label, + group: this.groupObject, + color: this.color, + hidden: this.hidden, + updated: this.updated, + pinned: this.pinned, + frame, + source: this.source, + }; + } + + save(frame, data) { + if (frame !== this.frame) { + throw new ScriptingError('Got frame is not equal to the frame of the shape'); + } + + if (this.lock && data.lock) { + return objectStateFactory.call(this, frame, this.get(frame)); + } + + const updated = data.updateFlags; + const fittedPoints = this._validateStateBeforeSave(frame, data, updated); + const { rotation } = data; + + // Now when all fields are validated, we can apply them + if (updated.label) { + this._saveLabel(data.label, frame); + } + + if (updated.attributes) { + this._saveAttributes(data.attributes, frame); + } + + if (updated.descriptions) { + this._saveDescriptions(data.descriptions); + } + + if (updated.points && fittedPoints.length) { + this._savePoints(fittedPoints, rotation, frame); + } + + if (updated.occluded) { + this._saveOccluded(data.occluded, frame); + } + + if (updated.zOrder) { + this._saveZOrder(data.zOrder, frame); + } + + if (updated.lock) { + this._saveLock(data.lock, frame); + } + + if (updated.pinned) { + this._savePinned(data.pinned, frame); + } + + if (updated.color) { + this._saveColor(data.color, frame); + } + + if (updated.hidden) { + this._saveHidden(data.hidden, frame); + } + + this.updateTimestamp(updated); + updated.reset(); + + return objectStateFactory.call(this, frame, this.get(frame)); + } + + _saveRLE(rle, frame) { + const undoPoints = this.points; + const undoRotation = this.rotation; + const redoPoints = points; + const redoRotation = rotation; + const undoSource = this.source; + const redoSource = Source.MANUAL; + + this.history.do( + HistoryActions.CHANGED_POINTS, + () => { + this.points = undoPoints; + this.source = undoSource; + this.rotation = undoRotation; + this.updated = Date.now(); + }, + () => { + this.points = redoPoints; + this.source = redoSource; + this.rotation = redoRotation; + this.updated = Date.now(); + }, + [this.clientID], + frame, + ); + + this.source = Source.MANUAL; + this.points = points; + this.rotation = rotation; + } + } + // eslint-disable-next-line no-underscore-dangle + Mask.prototype._saveOccluded = Shape.prototype._saveOccluded; + // eslint-disable-next-line no-underscore-dangle + Mask.prototype._saveZOrder = Shape.prototype._saveZOrder; + class Tag extends Annotation { // Method is used to export data to the server toJSON() { @@ -2169,6 +2328,7 @@ CuboidTrack, Track, Shape, + Mask, Tag, objectStateFactory, }; diff --git a/cvat-core/src/annotations.js b/cvat-core/src/annotations.js index a511f0f8a01..e401c775f86 100644 --- a/cvat-core/src/annotations.js +++ b/cvat-core/src/annotations.js @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021 Intel Corporation +// Copyright (C) 2019-2022 Intel Corporation // // SPDX-License-Identifier: MIT @@ -28,24 +28,24 @@ throw new ScriptingError(`Unknown session type was received ${sessionType}`); } - async function getAnnotationsFromServer(session) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + async function getAnnotationsFromServer(annotatableInstance) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (!cache.has(session)) { - const rawAnnotations = await serverProxy.annotations.getAnnotations(sessionType, session.id); + if (!cache.has(annotatableInstance)) { + const rawAnnotations = await serverProxy.annotations.getAnnotations(instanceType, annotatableInstance.id); // Get meta information about frames - const startFrame = sessionType === 'job' ? session.startFrame : 0; - const stopFrame = sessionType === 'job' ? session.stopFrame : session.size - 1; + const startFrame = instanceType === 'job' ? annotatableInstance.startFrame : 0; + const stopFrame = instanceType === 'job' ? annotatableInstance.stopFrame : annotatableInstance.size - 1; const frameMeta = {}; for (let i = startFrame; i <= stopFrame; i++) { - frameMeta[i] = await session.frames.get(i); + frameMeta[i] = await annotatableInstance.frames.get(i); } const history = new AnnotationsHistory(); const collection = new Collection({ - labels: session.labels || session.task.labels, + labels: annotatableInstance.labels || annotatableInstance.task.labels, history, startFrame, stopFrame, @@ -54,9 +54,9 @@ // eslint-disable-next-line no-unsanitized/method collection.import(rawAnnotations); - const saver = new AnnotationsSaver(rawAnnotations.version, collection, session); + const saver = new AnnotationsSaver(rawAnnotations.version, collection, annotatableInstance); - cache.set(session, { + cache.set(annotatableInstance, { collection, saver, history, @@ -64,44 +64,44 @@ } } - async function closeSession(session) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + async function clearCache(annotatableInstance) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - cache.delete(session); + if (cache.has(annotatableInstance)) { + cache.delete(annotatableInstance); } } - async function getAnnotations(session, frame, allTracks, filters) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + async function getAnnotations(annotatableInstance, frame, allTracks, filters) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - return cache.get(session).collection.get(frame, allTracks, filters); + if (cache.has(annotatableInstance)) { + return cache.get(annotatableInstance).collection.get(frame, allTracks, filters); } - await getAnnotationsFromServer(session); - return cache.get(session).collection.get(frame, allTracks, filters); + await getAnnotationsFromServer(annotatableInstance); + return cache.get(annotatableInstance).collection.get(frame, allTracks, filters); } - async function saveAnnotations(session, onUpdate) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + async function saveAnnotations(annotatableInstance, onUpdate) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - await cache.get(session).saver.save(onUpdate); + if (cache.has(annotatableInstance)) { + await cache.get(annotatableInstance).saver.save(onUpdate); } // If a collection wasn't uploaded, than it wasn't changed, finally we shouldn't save it } - function searchAnnotations(session, filters, frameFrom, frameTo) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + function searchAnnotations(annotatableInstance, filters, frameFrom, frameTo) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - return cache.get(session).collection.search(filters, frameFrom, frameTo); + if (cache.has(annotatableInstance)) { + return cache.get(annotatableInstance).collection.search(filters, frameFrom, frameTo); } throw new DataError( @@ -109,12 +109,12 @@ ); } - function searchEmptyFrame(session, frameFrom, frameTo) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + function searchEmptyFrame(annotatableInstance, frameFrom, frameTo) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - return cache.get(session).collection.searchEmpty(frameFrom, frameTo); + if (cache.has(annotatableInstance)) { + return cache.get(annotatableInstance).collection.searchEmpty(frameFrom, frameTo); } throw new DataError( @@ -122,12 +122,12 @@ ); } - function mergeAnnotations(session, objectStates) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + function mergeAnnotations(annotatableInstance, objectStates) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - return cache.get(session).collection.merge(objectStates); + if (cache.has(annotatableInstance)) { + return cache.get(annotatableInstance).collection.merge(objectStates); } throw new DataError( @@ -135,12 +135,12 @@ ); } - function splitAnnotations(session, objectState, frame) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + function splitAnnotations(annotatableInstance, objectState, frame) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - return cache.get(session).collection.split(objectState, frame); + if (cache.has(annotatableInstance)) { + return cache.get(annotatableInstance).collection.split(objectState, frame); } throw new DataError( @@ -148,12 +148,12 @@ ); } - function groupAnnotations(session, objectStates, reset) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + function groupAnnotations(annotatableInstance, objectStates, reset) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - return cache.get(session).collection.group(objectStates, reset); + if (cache.has(annotatableInstance)) { + return cache.get(annotatableInstance).collection.group(objectStates, reset); } throw new DataError( @@ -161,38 +161,38 @@ ); } - function hasUnsavedChanges(session) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + function hasUnsavedChanges(annotatableInstance) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - return cache.get(session).saver.hasUnsavedChanges(); + if (cache.has(annotatableInstance)) { + return cache.get(annotatableInstance).saver.hasUnsavedChanges(); } return false; } - async function clearAnnotations(session, reload, startframe, endframe, delTrackKeyframesOnly) { + async function clearAnnotations(annotatableInstance, reload, startframe, endframe, delTrackKeyframesOnly) { checkObjectType('reload', reload, 'boolean', null); - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - cache.get(session).collection.clear(startframe, endframe, delTrackKeyframesOnly); + if (cache.has(annotatableInstance)) { + cache.get(annotatableInstance).collection.clear(startframe, endframe, delTrackKeyframesOnly); } if (reload) { - cache.delete(session); - await getAnnotationsFromServer(session); + cache.delete(annotatableInstance); + await getAnnotationsFromServer(annotatableInstance); } } - function annotationsStatistics(session) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + function annotationsStatistics(annotatableInstance) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - return cache.get(session).collection.statistics(); + if (cache.has(annotatableInstance)) { + return cache.get(annotatableInstance).collection.statistics(); } throw new DataError( @@ -200,12 +200,12 @@ ); } - function putAnnotations(session, objectStates) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + function putAnnotations(annotatableInstance, objectStates) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - return cache.get(session).collection.put(objectStates); + if (cache.has(annotatableInstance)) { + return cache.get(annotatableInstance).collection.put(objectStates); } throw new DataError( @@ -213,12 +213,12 @@ ); } - function selectObject(session, objectStates, x, y) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + function selectObject(annotatableInstance, objectStates, x, y) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - return cache.get(session).collection.select(objectStates, x, y); + if (cache.has(annotatableInstance)) { + return cache.get(annotatableInstance).collection.select(objectStates, x, y); } throw new DataError( @@ -226,21 +226,21 @@ ); } - async function uploadAnnotations(session, file, loader) { - const sessionType = session instanceof Task ? 'task' : 'job'; + async function uploadAnnotations(annotatableInstance, file, loader) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; if (!(loader instanceof Loader)) { throw new ArgumentError('A loader must be instance of Loader class'); } - await serverProxy.annotations.uploadAnnotations(sessionType, session.id, file, loader.name); + await serverProxy.annotations.uploadAnnotations(instanceType, annotatableInstance.id, file, loader.name); } - function importAnnotations(session, data) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + function importAnnotations(annotatableInstance, data) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { + if (cache.has(annotatableInstance)) { // eslint-disable-next-line no-unsanitized/method - return cache.get(session).collection.import(data); + return cache.get(annotatableInstance).collection.import(data); } throw new DataError( @@ -248,12 +248,12 @@ ); } - function exportAnnotations(session) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + function exportAnnotations(annotatableInstance) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - return cache.get(session).collection.export(); + if (cache.has(annotatableInstance)) { + return cache.get(annotatableInstance).collection.export(); } throw new DataError( @@ -261,11 +261,14 @@ ); } - async function exportDataset(instance, format, name, saveImages = false) { + async function exportDataset(annotatableInstance, format, name, saveImages = false) { if (!(format instanceof String || typeof format === 'string')) { throw new ArgumentError('Format must be a string'); } - if (!(instance instanceof Task || instance instanceof Project || instance instanceof Job)) { + if (!(annotatableInstance instanceof Task || + annotatableInstance instanceof Project || + annotatableInstance instanceof Job + )) { throw new ArgumentError('A dataset can only be created from a job, task or project'); } if (typeof saveImages !== 'boolean') { @@ -273,22 +276,22 @@ } let result = null; - if (instance instanceof Task) { - result = await serverProxy.tasks.exportDataset(instance.id, format, name, saveImages); - } else if (instance instanceof Job) { - result = await serverProxy.tasks.exportDataset(instance.taskId, format, name, saveImages); + if (annotatableInstance instanceof Task) { + result = await serverProxy.tasks.exportDataset(annotatableInstance.id, format, name, saveImages); + } else if (annotatableInstance instanceof Job) { + result = await serverProxy.tasks.exportDataset(annotatableInstance.taskId, format, name, saveImages); } else { - result = await serverProxy.projects.exportDataset(instance.id, format, name, saveImages); + result = await serverProxy.projects.exportDataset(annotatableInstance.id, format, name, saveImages); } return result; } - function importDataset(instance, format, file, updateStatusCallback = () => {}) { + function importDataset(annotatableInstance, format, file, updateStatusCallback = () => {}) { if (!(typeof format === 'string')) { throw new ArgumentError('Format must be a string'); } - if (!(instance instanceof Project)) { + if (!(annotatableInstance instanceof Project)) { throw new ArgumentError('Instance should be a Project instance'); } if (!(typeof updateStatusCallback === 'function')) { @@ -297,15 +300,15 @@ if (!(['application/zip', 'application/x-zip-compressed'].includes(file.type))) { throw new ArgumentError('File should be file instance with ZIP extension'); } - return serverProxy.projects.importDataset(instance.id, format, file, updateStatusCallback); + return serverProxy.projects.importDataset(annotatableInstance.id, format, file, updateStatusCallback); } - function undoActions(session, count) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + function undoActions(annotatableInstance, count) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - return cache.get(session).history.undo(count); + if (cache.has(annotatableInstance)) { + return cache.get(annotatableInstance).history.undo(count); } throw new DataError( @@ -313,12 +316,12 @@ ); } - function redoActions(session, count) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + function redoActions(annotatableInstance, count) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - return cache.get(session).history.redo(count); + if (cache.has(annotatableInstance)) { + return cache.get(annotatableInstance).history.redo(count); } throw new DataError( @@ -326,12 +329,12 @@ ); } - function freezeHistory(session, frozen) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + function freezeHistory(annotatableInstance, frozen) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - return cache.get(session).history.freeze(frozen); + if (cache.has(annotatableInstance)) { + return cache.get(annotatableInstance).history.freeze(frozen); } throw new DataError( @@ -339,12 +342,12 @@ ); } - function clearActions(session) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + function clearActions(annotatableInstance) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - return cache.get(session).history.clear(); + if (cache.has(annotatableInstance)) { + return cache.get(annotatableInstance).history.clear(); } throw new DataError( @@ -352,12 +355,12 @@ ); } - function getActions(session) { - const sessionType = session instanceof Task ? 'task' : 'job'; - const cache = getCache(sessionType); + function getActions(annotatableInstance) { + const instanceType = annotatableInstance instanceof Task ? 'task' : 'job'; + const cache = getCache(instanceType); - if (cache.has(session)) { - return cache.get(session).history.get(); + if (cache.has(annotatableInstance)) { + return cache.get(annotatableInstance).history.get(); } throw new DataError( @@ -388,6 +391,6 @@ freezeHistory, clearActions, getActions, - closeSession, + clearCache, }; })(); diff --git a/cvat-core/src/enums.js b/cvat-core/src/enums.js index 88d7d5b5533..eed078617d5 100644 --- a/cvat-core/src/enums.js +++ b/cvat-core/src/enums.js @@ -141,12 +141,14 @@ * @name ObjectType * @memberof module:API.cvat.enums * @property {string} TAG 'tag' + * @property {string} MASK 'mask' * @property {string} SHAPE 'shape' * @property {string} TRACK 'track' * @readonly */ const ObjectType = Object.freeze({ TAG: 'tag', + MASK: 'mask', SHAPE: 'shape', TRACK: 'track', }); @@ -161,6 +163,7 @@ * @property {string} POLYLINE 'polyline' * @property {string} POINTS 'points' * @property {string} CUBOID 'cuboid' + * @property {string} MASK 'mask' * @readonly */ const ObjectShape = Object.freeze({ @@ -170,6 +173,7 @@ POINTS: 'points', ELLIPSE: 'ellipse', CUBOID: 'cuboid', + MASK: 'mask', }); /** diff --git a/cvat-core/src/session.js b/cvat-core/src/session.js index 5803fcc20b1..fe1afa76df8 100644 --- a/cvat-core/src/session.js +++ b/cvat-core/src/session.js @@ -1820,7 +1820,7 @@ freezeHistory, clearActions, getActions, - closeSession, + clearCache, } = require('./annotations'); buildDuplicatedAPI(Job.prototype); @@ -2088,17 +2088,17 @@ Job.prototype.close.implementation = function closeTask() { clearFrames(this.taskId); - closeSession(this); + clearCache(this); return this; }; Task.prototype.close.implementation = function closeTask() { clearFrames(this.id); for (const job of this.jobs) { - closeSession(job); + clearCache(job); } - closeSession(this); + clearCache(this); return this; }; From e54b279a37e163e6efa15c347a464b1ff6df2a4b Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Fri, 25 Mar 2022 23:33:00 +0300 Subject: [PATCH 002/106] tmp --- cvat-core/src/annotations-collection.js | 15 +-- cvat-core/src/enums.js | 1 - cvat-core/src/object-state.js | 2 +- cvat-ui/src/actions/annotation-actions.ts | 10 ++ cvat-ui/src/assets/brush.svg | 8 ++ cvat-ui/src/assets/eraser.svg | 8 ++ .../canvas/brush-toolbox-styles.scss | 45 +++++++ .../annotation-page/canvas/brush-tools.tsx | 117 ++++++++++++++++++ .../annotation-page/canvas/canvas-wrapper.tsx | 2 + .../annotation-page/canvas/draggable-hoc.tsx | 59 +++++++++ .../controls-side-bar/controls-side-bar.tsx | 9 +- .../controls-side-bar/draw-mask-control.tsx | 53 ++++++++ .../controls-side-bar/draw-mask-popover.tsx | 61 +++++++++ .../controls-side-bar/setup-tag-popover.tsx | 2 +- .../standard-workspace/styles.scss | 1 + .../controls-side-bar/draw-mask-popover.tsx | 114 +++++++++++++++++ cvat-ui/src/icons.tsx | 4 + cvat-ui/src/reducers/annotation-reducer.ts | 45 ++++--- cvat-ui/src/reducers/interfaces.ts | 7 ++ 19 files changed, 532 insertions(+), 31 deletions(-) create mode 100644 cvat-ui/src/assets/brush.svg create mode 100644 cvat-ui/src/assets/eraser.svg create mode 100644 cvat-ui/src/components/annotation-page/canvas/brush-toolbox-styles.scss create mode 100644 cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx create mode 100644 cvat-ui/src/components/annotation-page/canvas/draggable-hoc.tsx create mode 100644 cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-mask-control.tsx create mode 100644 cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover.tsx create mode 100644 cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover.tsx diff --git a/cvat-core/src/annotations-collection.js b/cvat-core/src/annotations-collection.js index 0e6314cd560..e132984e935 100644 --- a/cvat-core/src/annotations-collection.js +++ b/cvat-core/src/annotations-collection.js @@ -57,6 +57,8 @@ case 'cuboid': shapeModel = new CuboidShape(shapeData, clientID, color, injection); break; + case 'mask': + default: throw new DataError(`An unexpected type of shape "${type}"`); } @@ -137,7 +139,6 @@ tags: [], shapes: [], tracks: [], - masks: [], }; for (const tag of data.tags) { @@ -173,18 +174,6 @@ } } - for (const mask of data.masks) { - const clientID = ++this.count; - const color = colors[clientID % colors.length]; - const maskModel = new Mask(mask, clientID, color, this.injection); - if (maskModel) { - this.masks[maskModel.frame] = this.masks[maskModel.frame] || []; - this.masks[maskModel.frame].push(maskModel); - this.objects[clientID] = maskModel; - result.masks.push(maskModel); - } - } - return result; } diff --git a/cvat-core/src/enums.js b/cvat-core/src/enums.js index eed078617d5..1567e1c7edb 100644 --- a/cvat-core/src/enums.js +++ b/cvat-core/src/enums.js @@ -148,7 +148,6 @@ */ const ObjectType = Object.freeze({ TAG: 'tag', - MASK: 'mask', SHAPE: 'shape', TRACK: 'track', }); diff --git a/cvat-core/src/object-state.js b/cvat-core/src/object-state.js index d1fc8784908..633d8ece936 100644 --- a/cvat-core/src/object-state.js +++ b/cvat-core/src/object-state.js @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021 Intel Corporation +// Copyright (C) 2019-2022 Intel Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/actions/annotation-actions.ts b/cvat-ui/src/actions/annotation-actions.ts index 00869d1a319..b34949858d4 100644 --- a/cvat-ui/src/actions/annotation-actions.ts +++ b/cvat-ui/src/actions/annotation-actions.ts @@ -197,6 +197,7 @@ export enum AnnotationActionTypes { GET_CONTEXT_IMAGE_SUCCESS = 'GET_CONTEXT_IMAGE_SUCCESS', GET_CONTEXT_IMAGE_FAILED = 'GET_CONTEXT_IMAGE_FAILED', SWITCH_NAVIGATION_BLOCKED = 'SWITCH_NAVIGATION_BLOCKED', + UPDATE_BRUSH_TOOLS_CONFIG = 'UPDATE_BRUSH_TOOLS_CONFIG', } export function saveLogsAsync(): ThunkAction { @@ -308,6 +309,15 @@ export function updateCanvasContextMenu( }; } +export function updateCanvasBrushTools(config: { + visible?: boolean, left?: number, top?: number +}): AnyAction { + return { + type: AnnotationActionTypes.UPDATE_BRUSH_TOOLS_CONFIG, + payload: config, + }; +} + export function removeAnnotationsAsync( startFrame: number, endFrame: number, delTrackKeyframesOnly: boolean, ): ThunkAction { diff --git a/cvat-ui/src/assets/brush.svg b/cvat-ui/src/assets/brush.svg new file mode 100644 index 00000000000..f27768dc099 --- /dev/null +++ b/cvat-ui/src/assets/brush.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/cvat-ui/src/assets/eraser.svg b/cvat-ui/src/assets/eraser.svg new file mode 100644 index 00000000000..0386f9f7d70 --- /dev/null +++ b/cvat-ui/src/assets/eraser.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/cvat-ui/src/components/annotation-page/canvas/brush-toolbox-styles.scss b/cvat-ui/src/components/annotation-page/canvas/brush-toolbox-styles.scss new file mode 100644 index 00000000000..48367f68aa1 --- /dev/null +++ b/cvat-ui/src/components/annotation-page/canvas/brush-toolbox-styles.scss @@ -0,0 +1,45 @@ +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: MIT + +@import '../../../base.scss'; + +.cvat-brush-tools-toolbox { + position: absolute; + margin: $grid-unit-size; + padding: 0 $grid-unit-size; + border-radius: 4px; + background: $background-color-2; + display: flex; + align-items: center; + z-index: 100; + box-shadow: $box-shadow-base; + + > hr { + width: 1px; + height: $grid-unit-size * 4; + background: $border-color-1; + } + + > * { + margin-right: $grid-unit-size; + } + + .cvat-brush-tools-draggable-area { + font-size: 20px; + } + + .cvat-brush-tools-brush, + .cvat-brush-tools-eraser { + > .anticon { + svg { + width: 1em; + height: 1em; + } + } + } + + .cvat-brush-tools-active-tool { + background: $header-color; + } +} diff --git a/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx b/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx new file mode 100644 index 00000000000..39423089891 --- /dev/null +++ b/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx @@ -0,0 +1,117 @@ +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: MIT + +import './brush-toolbox-styles.scss'; + +import React, { useEffect, useState } from 'react'; +import ReactDOM from 'react-dom'; +import { useDispatch, useSelector } from 'react-redux'; +import Button from 'antd/lib/button'; +import Icon, { BgColorsOutlined, CheckOutlined, DragOutlined, PlusOutlined } from '@ant-design/icons'; +import InputNumber from 'antd/lib/input-number'; +import Select from 'antd/lib/select'; + +import CVATTooltip from 'components/common/cvat-tooltip'; +import { CombinedState } from 'reducers/interfaces'; +import { updateCanvasBrushTools } from 'actions/annotation-actions'; +import LabelSelector from 'components/label-selector/label-selector'; +import useDraggable from './draggable-hoc'; +import { BrushIcon, EraserIcon } from 'icons'; + +function BrushTools(): React.ReactPortal | null { + const defaultLabelID = useSelector((state: CombinedState) => state.annotation.drawing.activeLabelID); + const config = useSelector((state: CombinedState) => state.annotation.canvas.brushTools); + const { visible } = config; + const labels = useSelector((state: CombinedState) => state.annotation.job.labels); + const [activeLabelID, setActiveLabelID] = useState(null); + const [currentTool, setCurrentTool] = useState<'brush' | 'eraser' | 'fill'>('brush'); + const [[top, left], setTopLeft] = useState([0, 0]); + const dragBar = useDraggable( + (): number[] => { + const [element] = window.document.getElementsByClassName('cvat-brush-tools-toolbox'); + if (element) { + const { offsetTop, offsetLeft } = element as HTMLDivElement; + return [offsetTop, offsetLeft]; + } + + return [0, 0]; + }, + (newTop, newLeft) => setTopLeft([newTop, newLeft]), + ( +
+ +
+ ), + ); + + useEffect(() => { + setActiveLabelID(defaultLabelID); + }, [defaultLabelID]); + + useEffect(() => { + const canvasContainer = window.document.getElementsByClassName('cvat-canvas-container')[0]; + if (canvasContainer) { + const { offsetTop, offsetLeft } = canvasContainer.parentElement as HTMLElement; + setTopLeft([offsetTop, offsetLeft]); + } + }, []); + + if (!visible) { + return null; + } + + return ReactDOM.createPortal(( +
+
+ ), window.document.body); +} + +export default React.memo(BrushTools); diff --git a/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx b/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx index 6b1e8fbae13..dc2aac529f9 100644 --- a/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx @@ -19,6 +19,7 @@ import getCore from 'cvat-core-wrapper'; import consts from 'consts'; import CVATTooltip from 'components/common/cvat-tooltip'; import ImageSetupsContent from './image-setups-content'; +import BrushTools from './brush-tools'; import ContextImage from '../standard-workspace/context-image/context-image'; const cvat = getCore(); @@ -826,6 +827,7 @@ export default class CanvasWrapperComponent extends React.PureComponent { /> + }> diff --git a/cvat-ui/src/components/annotation-page/canvas/draggable-hoc.tsx b/cvat-ui/src/components/annotation-page/canvas/draggable-hoc.tsx new file mode 100644 index 00000000000..24ec75eaad3 --- /dev/null +++ b/cvat-ui/src/components/annotation-page/canvas/draggable-hoc.tsx @@ -0,0 +1,59 @@ +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: MIT + +import React, { useEffect } from 'react'; +import { useCallback } from 'react'; +import { useRef } from 'react'; +import { useLayoutEffect } from 'react'; + +export default function useDraggable( + getPosition: () => number[], + onDrag: (diffX: number, diffY: number) => void, + component: JSX.Element, +): JSX.Element { + const ref = useRef(null); + useEffect(() => { + const click = [0, 0]; + const position = getPosition(); + + const mouseMoveListener = (event: MouseEvent): void => { + const dy = event.clientY - click[0]; + const dx = event.clientX - click[1]; + onDrag(position[0] + dy, position[1] + dx); + event.stopPropagation(); + }; + + const mouseDownListener = (event: MouseEvent): void => { + const [initialTop, initialLeft] = getPosition(); + position[0] = initialTop; + position[1] = initialLeft; + click[0] = event.clientY; + click[1] = event.clientX; + window.addEventListener('mousemove', mouseMoveListener); + event.stopPropagation(); + }; + + const mouseUpListener = (): void => { + window.removeEventListener('mousemove', mouseMoveListener); + }; + + window.document.addEventListener('mouseup', mouseUpListener); + if (ref.current) { + ref.current.addEventListener('mousedown', mouseDownListener); + } + + return () => { + window.document.removeEventListener('mouseup', mouseUpListener); + if (ref.current) { + ref.current.removeEventListener('mousedown', mouseDownListener); + } + }; + }, []); + + return ( +
+ {component} +
+ ); +} diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx index 606cf9c6d14..29255944506 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2021 Intel Corporation +// Copyright (C) 2022 Intel Corporation // // SPDX-License-Identifier: MIT @@ -23,6 +23,7 @@ import DrawPolylineControl, { Props as DrawPolylineControlProps } from './draw-p import DrawPointsControl, { Props as DrawPointsControlProps } from './draw-points-control'; import DrawEllipseControl, { Props as DrawEllipseControlProps } from './draw-ellipse-control'; import DrawCuboidControl, { Props as DrawCuboidControlProps } from './draw-cuboid-control'; +import DrawMaskControl, { Props as DrawMaskControlProps } from './draw-mask-control'; import SetupTagControl, { Props as SetupTagControlProps } from './setup-tag-control'; import MergeControl, { Props as MergeControlProps } from './merge-control'; import GroupControl, { Props as GroupControlProps } from './group-control'; @@ -60,6 +61,7 @@ const ObservedDrawPolylineControl = ControlVisibilityObserver(DrawPointsControl); const ObservedDrawEllipseControl = ControlVisibilityObserver(DrawEllipseControl); const ObservedDrawCuboidControl = ControlVisibilityObserver(DrawCuboidControl); +const ObservedDrawMaskControl = ControlVisibilityObserver(DrawMaskControl); const ObservedSetupTagControl = ControlVisibilityObserver(SetupTagControl); const ObservedMergeControl = ControlVisibilityObserver(MergeControl); const ObservedGroupControl = ControlVisibilityObserver(GroupControl); @@ -253,6 +255,11 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element { isDrawing={activeControl === ActiveControl.DRAW_CUBOID} disabled={!labels.length} /> +
diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-mask-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-mask-control.tsx new file mode 100644 index 00000000000..2ef200b6709 --- /dev/null +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-mask-control.tsx @@ -0,0 +1,53 @@ +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: MIT + +import React from 'react'; +import Popover from 'antd/lib/popover'; +import Icon from '@ant-design/icons'; + +import { Canvas } from 'cvat-canvas-wrapper'; +import { BrushIcon } from 'icons'; + +import DrawShapePopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover'; +import withVisibilityHandling from './handle-popover-visibility'; + +export interface Props { + canvasInstance: Canvas; + isDrawing: boolean; + disabled?: boolean; +} + +const CustomPopover = withVisibilityHandling(Popover, 'draw-mask'); +function DrawPointsControl(props: Props): JSX.Element { + const { canvasInstance, isDrawing, disabled } = props; + const dynamicPopoverProps = isDrawing ? { + overlayStyle: { + display: 'none', + }, + } : {}; + + const dynamicIconProps = isDrawing ? { + className: 'cvat-draw-mask-control cvat-active-canvas-control', + onClick: (): void => { + canvasInstance.draw({ enabled: false }); + }, + } : { + className: 'cvat-draw-mask-control', + }; + + return disabled ? ( + + ) : ( + } + > + + + ); +} + +export default React.memo(DrawPointsControl); diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover.tsx new file mode 100644 index 00000000000..615ad5c043b --- /dev/null +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover.tsx @@ -0,0 +1,61 @@ +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: MIT + +import React from 'react'; +import { Row, Col } from 'antd/lib/grid'; +import Button from 'antd/lib/button'; +import Text from 'antd/lib/typography/Text'; + +import LabelSelector from 'components/label-selector/label-selector'; +import CVATTooltip from 'components/common/cvat-tooltip'; + +interface Props { + labels: any[]; + selectedLabelID: number; + repeatShapeShortcut: string; + onChangeLabel(value: string): void; + onDraw(labelID: number): void; +} + +function DrawMaskPopover(props: Props): JSX.Element { + const { + labels, selectedLabelID, repeatShapeShortcut, onChangeLabel, onDraw, + } = props; + + return ( +
+ + + + Draw new mask + + + + + + Label + + + + + + + + + + + + + + +
+ ); +} + +export default React.memo(DrawMaskPopover); diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/setup-tag-popover.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/setup-tag-popover.tsx index a202e3467a4..f5fed521725 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/setup-tag-popover.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/setup-tag-popover.tsx @@ -50,7 +50,7 @@ function SetupTagPopover(props: Props): JSX.Element { - + diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/styles.scss b/cvat-ui/src/components/annotation-page/standard-workspace/styles.scss index 78bc89c807e..8721e1b1c12 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/styles.scss +++ b/cvat-ui/src/components/annotation-page/standard-workspace/styles.scss @@ -115,6 +115,7 @@ .cvat-draw-polyline-control, .cvat-draw-points-control, .cvat-draw-ellipse-control, +.cvat-draw-mask-control, .cvat-draw-cuboid-control, .cvat-setup-tag-control, .cvat-merge-control, diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover.tsx new file mode 100644 index 00000000000..0deef5ad24f --- /dev/null +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover.tsx @@ -0,0 +1,114 @@ +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: MIT + +import React from 'react'; +import { connect } from 'react-redux'; + +import DrawMaskPopoverComponent from 'components/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover'; +import { rememberObject, updateCanvasBrushTools } from 'actions/annotation-actions'; +import { CombinedState, ShapeType, ObjectType } from 'reducers/interfaces'; +import { Canvas } from 'cvat-canvas-wrapper'; + +interface DispatchToProps { + onDrawStart( + shapeType: ShapeType, + labelID: number, + objectType: ObjectType, + ): void; +} + +interface StateToProps { + normalizedKeyMap: Record; + canvasInstance: Canvas; + labels: any[]; +} + +function mapDispatchToProps(dispatch: any): DispatchToProps { + return { + onDrawStart( + shapeType: ShapeType, + labelID: number, + objectType: ObjectType, + ): void { + dispatch( + rememberObject({ + activeObjectType: objectType, + activeShapeType: shapeType, + activeLabelID: labelID, + }), + ); + + dispatch( + updateCanvasBrushTools({ + visible: true, + }), + ); + }, + }; +} + +function mapStateToProps(state: CombinedState): StateToProps { + const { + annotation: { + canvas: { instance: canvasInstance }, + job: { labels }, + }, + shortcuts: { normalizedKeyMap }, + } = state; + + return { + canvasInstance: canvasInstance as Canvas, + normalizedKeyMap, + labels, + }; +} + +type Props = StateToProps & DispatchToProps; + +interface State { + selectedLabelID: number; +} + +class DrawShapePopoverContainer extends React.PureComponent { + constructor(props: Props) { + super(props); + const defaultLabelID = props.labels.length ? props.labels[0].id : null; + this.state = { selectedLabelID: defaultLabelID }; + } + + private onDraw = (): void => { + const { canvasInstance, onDrawStart } = this.props; + const { selectedLabelID } = this.state; + + canvasInstance.cancel(); + canvasInstance.draw({ + enabled: true, + shapeType: ShapeType.MASK, + crosshair: false, + }); + + onDrawStart(ShapeType.MASK, selectedLabelID, ObjectType.SHAPE); + }; + + private onChangeLabel = (value: any): void => { + this.setState({ selectedLabelID: value.id }); + }; + + public render(): JSX.Element { + const { selectedLabelID } = this.state; + const { normalizedKeyMap, labels } = this.props; + + return ( + + ); + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(DrawShapePopoverContainer); diff --git a/cvat-ui/src/icons.tsx b/cvat-ui/src/icons.tsx index 02bbf865d84..e5fcf209ad7 100644 --- a/cvat-ui/src/icons.tsx +++ b/cvat-ui/src/icons.tsx @@ -51,6 +51,8 @@ import SVGCVATAzureProvider from './assets/vscode-icons_file-type-azure.svg'; import SVGCVATS3Provider from './assets/S3.svg'; import SVGCVATGoogleCloudProvider from './assets/google-cloud.svg'; import SVGOpenVINO from './assets/openvino.svg'; +import SVGBrushIcon from './assets/brush.svg'; +import SVGEraserIcon from './assets/eraser.svg'; export const CVATLogo = React.memo((): JSX.Element => ); export const CursorIcon = React.memo((): JSX.Element => ); @@ -99,3 +101,5 @@ export const AzureProvider = React.memo((): JSX.Element => ); export const GoogleCloudProvider = React.memo((): JSX.Element => ); export const OpenVINOIcon = React.memo((): JSX.Element => ); +export const BrushIcon = React.memo((): JSX.Element => ); +export const EraserIcon = React.memo((): JSX.Element => ); diff --git a/cvat-ui/src/reducers/annotation-reducer.ts b/cvat-ui/src/reducers/annotation-reducer.ts index a01e895093e..48e8a804ee9 100644 --- a/cvat-ui/src/reducers/annotation-reducer.ts +++ b/cvat-ui/src/reducers/annotation-reducer.ts @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2021 Intel Corporation +// Copyright (C) 2020-2022 Intel Corporation // // SPDX-License-Identifier: MIT @@ -39,6 +39,11 @@ const defaultState: AnnotationState = { pointID: null, clientID: null, }, + brushTools: { + visible: true, + top: 0, + left: 0, + }, instance: null, ready: false, activeControl: ActiveControl.CURSOR, @@ -470,20 +475,20 @@ export default (state = defaultState, action: AnyAction): AnnotationState => { const { payload } = action; let { activeControl } = state.canvas; - if (payload.activeShapeType === ShapeType.RECTANGLE) { - activeControl = ActiveControl.DRAW_RECTANGLE; - } else if (payload.activeShapeType === ShapeType.POLYGON) { - activeControl = ActiveControl.DRAW_POLYGON; - } else if (payload.activeShapeType === ShapeType.POLYLINE) { - activeControl = ActiveControl.DRAW_POLYLINE; - } else if (payload.activeShapeType === ShapeType.POINTS) { - activeControl = ActiveControl.DRAW_POINTS; - } else if (payload.activeShapeType === ShapeType.ELLIPSE) { - activeControl = ActiveControl.DRAW_ELLIPSE; - } else if (payload.activeShapeType === ShapeType.CUBOID) { - activeControl = ActiveControl.DRAW_CUBOID; - } else if (payload.activeObjectType === ObjectType.TAG) { + const controlMapping = { + [ShapeType.RECTANGLE]: ActiveControl.DRAW_RECTANGLE, + [ShapeType.POLYGON]: ActiveControl.DRAW_POLYGON, + [ShapeType.POLYLINE]: ActiveControl.DRAW_POLYLINE, + [ShapeType.POINTS]: ActiveControl.DRAW_POINTS, + [ShapeType.ELLIPSE]: ActiveControl.DRAW_ELLIPSE, + [ShapeType.CUBOID]: ActiveControl.DRAW_CUBOID, + [ShapeType.MASK]: ActiveControl.DRAW_MASK, + }; + + if (payload.activeObjectType === ObjectType.TAG) { activeControl = ActiveControl.CURSOR; + } else { + activeControl = controlMapping[payload.activeShapeType as ShapeType]; } return { @@ -956,6 +961,18 @@ export default (state = defaultState, action: AnyAction): AnnotationState => { }, }; } + case AnnotationActionTypes.UPDATE_BRUSH_TOOLS_CONFIG: { + return { + ...state, + canvas: { + ...state.canvas, + brushTools: { + ...state.canvas.brushTools, + ...action.payload, + }, + }, + }; + } case AnnotationActionTypes.REDO_ACTION_SUCCESS: case AnnotationActionTypes.UNDO_ACTION_SUCCESS: { const { activatedStateID } = state.annotations; diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts index 64d2d4661ea..46766a7facf 100644 --- a/cvat-ui/src/reducers/interfaces.ts +++ b/cvat-ui/src/reducers/interfaces.ts @@ -479,6 +479,7 @@ export enum ActiveControl { DRAW_POLYLINE = 'draw_polyline', DRAW_POINTS = 'draw_points', DRAW_ELLIPSE = 'draw_ellipse', + DRAW_MASK = 'draw_mask', DRAW_CUBOID = 'draw_cuboid', MERGE = 'merge', GROUP = 'group', @@ -497,6 +498,7 @@ export enum ShapeType { POINTS = 'points', ELLIPSE = 'ellipse', CUBOID = 'cuboid', + MASK = 'mask', } export enum ObjectType { @@ -550,6 +552,11 @@ export interface AnnotationState { pointID: number | null; clientID: number | null; }; + brushTools: { + visible: boolean; + top: number; + left: number; + }; instance: Canvas | Canvas3d | null; ready: boolean; activeControl: ActiveControl; From 23905d6e704c4efa0718279737ffadc5f149950d Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 29 Mar 2022 01:31:09 +0300 Subject: [PATCH 003/106] Basic usage of fabric.js --- cvat-canvas/package-lock.json | 2391 ++++++++++++++++- cvat-canvas/package.json | 4 + cvat-canvas/src/typescript/canvasModel.ts | 14 +- cvat-canvas/src/typescript/canvasView.ts | 41 +- .../src/typescript/masksDrawHandler.ts | 104 + .../annotation-page/canvas/brush-tools.tsx | 122 +- .../annotation-page/canvas/draggable-hoc.tsx | 12 +- cvat-ui/src/reducers/annotation-reducer.ts | 2 +- 8 files changed, 2574 insertions(+), 116 deletions(-) create mode 100644 cvat-canvas/src/typescript/masksDrawHandler.ts diff --git a/cvat-canvas/package-lock.json b/cvat-canvas/package-lock.json index 4c915d045a7..3fd4ff97f4c 100644 --- a/cvat-canvas/package-lock.json +++ b/cvat-canvas/package-lock.json @@ -10,99 +10,2072 @@ "license": "MIT", "dependencies": { "@types/polylabel": "^1.0.5", + "fabric": "^5.2.1", "polylabel": "^1.1.0", "svg.draggable.js": "2.2.2", "svg.draw.js": "^2.0.4", "svg.js": "2.7.1", "svg.resize.js": "1.4.3", "svg.select.js": "3.0.1" + }, + "devDependencies": { + "@types/fabric": "^4.5.7" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz", + "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.5", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "optional": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/fabric": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/@types/fabric/-/fabric-4.5.7.tgz", + "integrity": "sha512-GkILNUXudoHWlOgiZRlQAU/u/HOQqB6YK6tRLIGtbCIRad9CLFpFyb6ys62FZawPZ/lSq7wLeRvfqOOt2X2BTQ==", + "dev": true + }, + "node_modules/@types/polylabel": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/polylabel/-/polylabel-1.0.5.tgz", + "integrity": "sha512-gnaNmo1OJiYNBFAZMZdqLZ3hKx2ee4ksAzqhKWBxuQ61PmhINHMcvIqsGmyCD1WFKCkwRt9NFhMSmKE6AgYY+w==" + }, + "node_modules/abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "optional": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true + }, + "node_modules/acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "optional": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "optional": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "optional": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "optional": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "optional": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "optional": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "optional": true + }, + "node_modules/canvas": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.9.1.tgz", + "integrity": "sha512-vSQti1uG/2gjv3x6QLOZw7TctfufaerTWbVe+NSduHxxLGB+qf3kFgQ6n66DSnuoINtVUjrLLIK2R+lxrBG07A==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.15.0", + "simple-get": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "optional": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "optional": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "optional": true + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "optional": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "optional": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "optional": true + }, + "node_modules/data-urls": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.1.tgz", + "integrity": "sha512-Ds554NeT5Gennfoo9KN50Vh6tpgtvYEwraYjejXnyTpu1C7oXKxdFk75REooENHE8ndTVOJuv+BEs4/J/xcozw==", + "optional": true, + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^10.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "optional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "optional": true + }, + "node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "optional": true, + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "optional": true + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "optional": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "optional": true + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "optional": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "optional": true + }, + "node_modules/escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "optional": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "optional": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "optional": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fabric": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/fabric/-/fabric-5.2.1.tgz", + "integrity": "sha512-Irltx4i+aLccWgdQj2Uvrwh/XulDAqqYMZ1bI13fAtmlxl4ggobo0t7VVYy3Ob4YEB0sCeJZKE8ExZgGo/amkw==", + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "canvas": "^2.8.0", + "jsdom": "^19.0.0" + } + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "optional": true + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "optional": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "optional": true + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "optional": true + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "optional": true, + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "optional": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "optional": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "optional": true + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "optional": true + }, + "node_modules/jsdom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-19.0.0.tgz", + "integrity": "sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==", + "optional": true, + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.5.0", + "acorn-globals": "^6.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.1", + "decimal.js": "^10.3.1", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^3.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^10.0.0", + "ws": "^8.2.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "optional": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "optional": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "optional": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "optional": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + }, + "node_modules/nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "optional": true + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "optional": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "optional": true + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "optional": true + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "optional": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "optional": true, + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "optional": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "optional": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "optional": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "optional": true + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/polylabel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/polylabel/-/polylabel-1.1.0.tgz", + "integrity": "sha512-bxaGcA40sL3d6M4hH72Z4NdLqxpXRsCFk8AITYg6x1rn1Ei3izf00UMLklerBZTO49aPA3CYrIwVulx2Bce2pA==", + "dependencies": { + "tinyqueue": "^2.0.3" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "optional": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "optional": true + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "optional": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "optional": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "optional": true + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "optional": true + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "optional": true, + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "optional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "optional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/svg.draggable.js": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", + "integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==", + "dependencies": { + "svg.js": "^2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.draw.js": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg.draw.js/-/svg.draw.js-2.0.4.tgz", + "integrity": "sha512-NMbecB0vg11AP76B0aLfI2cX7g9WurPM8x3yKxuJ9feM1vkI1GVjWZZjWpo3mkEzB1UJ8pKngaPaUCIOGi8uUA==", + "dependencies": { + "svg.js": "2.x.x" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz", + "integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==" + }, + "node_modules/svg.resize.js": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz", + "integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==", + "dependencies": { + "svg.js": "^2.6.5", + "svg.select.js": "^2.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.resize.js/node_modules/svg.select.js": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz", + "integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==", + "dependencies": { + "svg.js": "^2.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.select.js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz", + "integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==", + "dependencies": { + "svg.js": "^2.6.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "optional": true + }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "optional": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" + }, + "node_modules/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "optional": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "optional": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "optional": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "optional": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "optional": true + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "optional": true, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz", + "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==", + "optional": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "optional": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-10.0.0.tgz", + "integrity": "sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==", + "optional": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "optional": true + }, + "node_modules/ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "optional": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "optional": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "optional": true + } + }, + "dependencies": { + "@mapbox/node-pre-gyp": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz", + "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==", + "optional": true, + "requires": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.5", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + } + }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "optional": true + }, + "@types/fabric": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/@types/fabric/-/fabric-4.5.7.tgz", + "integrity": "sha512-GkILNUXudoHWlOgiZRlQAU/u/HOQqB6YK6tRLIGtbCIRad9CLFpFyb6ys62FZawPZ/lSq7wLeRvfqOOt2X2BTQ==", + "dev": true + }, + "@types/polylabel": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/polylabel/-/polylabel-1.0.5.tgz", + "integrity": "sha512-gnaNmo1OJiYNBFAZMZdqLZ3hKx2ee4ksAzqhKWBxuQ61PmhINHMcvIqsGmyCD1WFKCkwRt9NFhMSmKE6AgYY+w==" + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "optional": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true + }, + "acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "optional": true + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "optional": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "optional": true + } + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "optional": true + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "optional": true, + "requires": { + "debug": "4" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "optional": true + }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "optional": true + }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "optional": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@types/polylabel": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/polylabel/-/polylabel-1.0.5.tgz", - "integrity": "sha512-gnaNmo1OJiYNBFAZMZdqLZ3hKx2ee4ksAzqhKWBxuQ61PmhINHMcvIqsGmyCD1WFKCkwRt9NFhMSmKE6AgYY+w==" + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "optional": true }, - "node_modules/polylabel": { + "canvas": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.9.1.tgz", + "integrity": "sha512-vSQti1uG/2gjv3x6QLOZw7TctfufaerTWbVe+NSduHxxLGB+qf3kFgQ6n66DSnuoINtVUjrLLIK2R+lxrBG07A==", + "optional": true, + "requires": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.15.0", + "simple-get": "^3.0.3" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "optional": true + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "optional": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "optional": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "optional": true + }, + "console-control-strings": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/polylabel/-/polylabel-1.1.0.tgz", - "integrity": "sha512-bxaGcA40sL3d6M4hH72Z4NdLqxpXRsCFk8AITYg6x1rn1Ei3izf00UMLklerBZTO49aPA3CYrIwVulx2Bce2pA==", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "optional": true + }, + "cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "optional": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "optional": true, + "requires": { + "cssom": "~0.3.6" + }, "dependencies": { - "tinyqueue": "^2.0.3" + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "optional": true + } } }, - "node_modules/svg.draggable.js": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", - "integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==", - "dependencies": { - "svg.js": "^2.0.1" - }, - "engines": { - "node": ">= 0.8.0" + "data-urls": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.1.tgz", + "integrity": "sha512-Ds554NeT5Gennfoo9KN50Vh6tpgtvYEwraYjejXnyTpu1C7oXKxdFk75REooENHE8ndTVOJuv+BEs4/J/xcozw==", + "optional": true, + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^10.0.0" } }, - "node_modules/svg.draw.js": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "optional": true, + "requires": { + "ms": "2.1.2" + } + }, + "decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "optional": true + }, + "decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "optional": true, + "requires": { + "mimic-response": "^2.0.0" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "optional": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "optional": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "optional": true + }, + "domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "optional": true, + "requires": { + "webidl-conversions": "^7.0.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "optional": true + }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "optional": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "optional": true + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "optional": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "optional": true + }, + "fabric": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/fabric/-/fabric-5.2.1.tgz", + "integrity": "sha512-Irltx4i+aLccWgdQj2Uvrwh/XulDAqqYMZ1bI13fAtmlxl4ggobo0t7VVYy3Ob4YEB0sCeJZKE8ExZgGo/amkw==", + "requires": { + "canvas": "^2.8.0", + "jsdom": "^19.0.0" + } + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "optional": true + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "optional": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "optional": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "optional": true + }, + "gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "optional": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "optional": true + }, + "html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "optional": true, + "requires": { + "whatwg-encoding": "^2.0.0" + } + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "optional": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "optional": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg.draw.js/-/svg.draw.js-2.0.4.tgz", - "integrity": "sha512-NMbecB0vg11AP76B0aLfI2cX7g9WurPM8x3yKxuJ9feM1vkI1GVjWZZjWpo3mkEzB1UJ8pKngaPaUCIOGi8uUA==", - "dependencies": { - "svg.js": "2.x.x" - }, - "engines": { - "node": ">= 0.8.0" + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "optional": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "optional": true + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "optional": true + }, + "jsdom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-19.0.0.tgz", + "integrity": "sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==", + "optional": true, + "requires": { + "abab": "^2.0.5", + "acorn": "^8.5.0", + "acorn-globals": "^6.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.1", + "decimal.js": "^10.3.1", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^3.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^10.0.0", + "ws": "^8.2.3", + "xml-name-validator": "^4.0.0" } }, - "node_modules/svg.js": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz", - "integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==" + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "optional": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } }, - "node_modules/svg.resize.js": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz", - "integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==", - "dependencies": { - "svg.js": "^2.6.5", - "svg.select.js": "^2.1.2" + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "optional": true, + "requires": { + "semver": "^6.0.0" }, - "engines": { - "node": ">= 0.8.0" + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "optional": true + } } }, - "node_modules/svg.resize.js/node_modules/svg.select.js": { + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "optional": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "optional": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "optional": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz", - "integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==", - "dependencies": { - "svg.js": "^2.2.5" - }, - "engines": { - "node": ">= 0.8.0" + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "optional": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" } }, - "node_modules/svg.select.js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz", - "integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==", - "dependencies": { - "svg.js": "^2.6.5" + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + }, + "nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "optional": true + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "optional": true, + "requires": { + "whatwg-url": "^5.0.0" }, - "engines": { - "node": ">= 0.8.0" + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "optional": true + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "optional": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "optional": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } } }, - "node_modules/tinyqueue": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", - "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" - } - }, - "dependencies": { - "@types/polylabel": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/polylabel/-/polylabel-1.0.5.tgz", - "integrity": "sha512-gnaNmo1OJiYNBFAZMZdqLZ3hKx2ee4ksAzqhKWBxuQ61PmhINHMcvIqsGmyCD1WFKCkwRt9NFhMSmKE6AgYY+w==" + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "optional": true, + "requires": { + "abbrev": "1" + } + }, + "npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "optional": true, + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "optional": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "optional": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "optional": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "optional": true }, "polylabel": { "version": "1.1.0", @@ -112,6 +2085,138 @@ "tinyqueue": "^2.0.3" } }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "optional": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "optional": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "optional": true + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "optional": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "optional": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "optional": true + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "optional": true + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "optional": true + }, + "simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "optional": true, + "requires": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "optional": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "optional": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "optional": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "svg.draggable.js": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", @@ -160,10 +2265,166 @@ "svg.js": "^2.6.5" } }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "optional": true + }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "optional": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, "tinyqueue": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" + }, + "tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "optional": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + } + }, + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "optional": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "optional": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "optional": true + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "optional": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz", + "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==", + "optional": true, + "requires": { + "xml-name-validator": "^4.0.0" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "optional": true + }, + "whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "optional": true, + "requires": { + "iconv-lite": "0.6.3" + } + }, + "whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "optional": true + }, + "whatwg-url": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-10.0.0.tgz", + "integrity": "sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==", + "optional": true, + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "optional": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "optional": true + }, + "ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "optional": true, + "requires": {} + }, + "xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "optional": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "optional": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "optional": true } } } diff --git a/cvat-canvas/package.json b/cvat-canvas/package.json index 4c5d7da82d5..54e3a249ffb 100644 --- a/cvat-canvas/package.json +++ b/cvat-canvas/package.json @@ -17,11 +17,15 @@ ], "dependencies": { "@types/polylabel": "^1.0.5", + "fabric": "^5.2.1", "polylabel": "^1.1.0", "svg.draggable.js": "2.2.2", "svg.draw.js": "^2.0.4", "svg.js": "2.7.1", "svg.resize.js": "1.4.3", "svg.select.js": "3.0.1" + }, + "devDependencies": { + "@types/fabric": "^4.5.7" } } diff --git a/cvat-canvas/src/typescript/canvasModel.ts b/cvat-canvas/src/typescript/canvasModel.ts index bc43af8fec4..ddbab5ba0c7 100644 --- a/cvat-canvas/src/typescript/canvasModel.ts +++ b/cvat-canvas/src/typescript/canvasModel.ts @@ -67,6 +67,15 @@ export interface Configuration { creationOpacity?: number; } +export interface BrushTool { + type: 'brush' | 'eraser' | 'fill'; + color: string; + removeUnderlyingPixels?: boolean; + form?: 'circle' | 'square'; + fillThreshold?: number; + size?: number; +} + export interface DrawData { enabled: boolean; shapeType?: string; @@ -75,6 +84,7 @@ export interface DrawData { numberOfPoints?: number; initialState?: any; crosshair?: boolean; + brushTool?: BrushTool | null; redraw?: number; } @@ -530,9 +540,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { } if (drawData.enabled) { - if (this.data.drawData.enabled) { - throw new Error('Drawing has been already started'); - } else if (!drawData.shapeType && !drawData.initialState) { + if (!drawData.shapeType && !drawData.initialState) { throw new Error('A shape type is not specified'); } else if (typeof drawData.numberOfPoints !== 'undefined') { if (drawData.shapeType === 'polygon' && drawData.numberOfPoints < 3) { diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 4cc1377f54b..2fff1518220 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -12,6 +12,7 @@ import 'svg.select.js'; import { CanvasController } from './canvasController'; import { Listener, Master } from './master'; import { DrawHandler, DrawHandlerImpl } from './drawHandler'; +import { MasksDrawHandler, MasksDrawHandlerImpl } from './masksDrawHandler'; import { EditHandler, EditHandlerImpl } from './editHandler'; import { MergeHandler, MergeHandlerImpl } from './mergeHandler'; import { SplitHandler, SplitHandlerImpl } from './splitHandler'; @@ -62,6 +63,7 @@ export class CanvasViewImpl implements CanvasView, Listener { private text: SVGSVGElement; private adoptedText: SVG.Container; private background: HTMLCanvasElement; + private masksContent: HTMLCanvasElement; private bitmap: HTMLCanvasElement; private grid: SVGSVGElement; private content: SVGSVGElement; @@ -79,6 +81,7 @@ export class CanvasViewImpl implements CanvasView, Listener { private drawnIssueRegions: Record; private geometry: Geometry; private drawHandler: DrawHandler; + private masksDrawHandler: MasksDrawHandler; private editHandler: EditHandler; private mergeHandler: MergeHandler; private splitHandler: SplitHandler; @@ -506,6 +509,7 @@ export class CanvasViewImpl implements CanvasView, Listener { // Transform handlers this.drawHandler.transform(this.geometry); + this.masksDrawHandler.transform(this.geometry); this.editHandler.transform(this.geometry); this.zoomHandler.transform(this.geometry); this.autoborderHandler.transform(this.geometry); @@ -515,7 +519,13 @@ export class CanvasViewImpl implements CanvasView, Listener { private transformCanvas(): void { // Transform canvas - for (const obj of [this.background, this.grid, this.content, this.bitmap, this.attachmentBoard]) { + for (const obj of [ + this.background, + this.grid, + this.content, + this.bitmap, + this.attachmentBoard, + ]) { obj.style.transform = `scale(${this.geometry.scale}) rotate(${this.geometry.angle}deg)`; } @@ -587,6 +597,7 @@ export class CanvasViewImpl implements CanvasView, Listener { // Transform handlers this.drawHandler.transform(this.geometry); + this.masksDrawHandler.transform(this.geometry); this.editHandler.transform(this.geometry); this.zoomHandler.transform(this.geometry); this.autoborderHandler.transform(this.geometry); @@ -595,7 +606,7 @@ export class CanvasViewImpl implements CanvasView, Listener { } private resizeCanvas(): void { - for (const obj of [this.background, this.grid, this.bitmap]) { + for (const obj of [this.background, this.masksContent, this.grid, this.bitmap]) { obj.style.width = `${this.geometry.image.width}px`; obj.style.height = `${this.geometry.image.height}px`; } @@ -994,6 +1005,7 @@ export class CanvasViewImpl implements CanvasView, Listener { this.text = window.document.createElementNS('http://www.w3.org/2000/svg', 'svg'); this.adoptedText = SVG.adopt((this.text as any) as HTMLElement) as SVG.Container; this.background = window.document.createElement('canvas'); + this.masksContent = window.document.createElement('canvas'); this.bitmap = window.document.createElement('canvas'); // window.document.createElementNS('http://www.w3.org/2000/svg', 'svg'); @@ -1059,6 +1071,7 @@ export class CanvasViewImpl implements CanvasView, Listener { // Setup content this.text.setAttribute('id', 'cvat_canvas_text_content'); this.background.setAttribute('id', 'cvat_canvas_background'); + this.masksContent.setAttribute('id', 'cvat_canvas_masks_content'); this.content.setAttribute('id', 'cvat_canvas_content'); this.bitmap.setAttribute('id', 'cvat_canvas_bitmap'); this.bitmap.style.display = 'none'; @@ -1080,6 +1093,7 @@ export class CanvasViewImpl implements CanvasView, Listener { this.canvas.appendChild(this.loadingAnimation); this.canvas.appendChild(this.text); this.canvas.appendChild(this.background); + this.canvas.appendChild(this.masksContent); this.canvas.appendChild(this.bitmap); this.canvas.appendChild(this.grid); this.canvas.appendChild(this.content); @@ -1095,6 +1109,10 @@ export class CanvasViewImpl implements CanvasView, Listener { this.geometry, this.configuration, ); + this.masksDrawHandler = new MasksDrawHandlerImpl( + this.onDrawDone.bind(this), + this.masksContent, + ); this.editHandler = new EditHandlerImpl(this.onEditDone.bind(this), this.adoptedContent, this.autoborderHandler); this.mergeHandler = new MergeHandlerImpl( this.onMergeDone.bind(this), @@ -1126,12 +1144,12 @@ export class CanvasViewImpl implements CanvasView, Listener { ); // Setup event handlers - this.content.addEventListener('dblclick', (e: MouseEvent): void => { + this.canvas.addEventListener('dblclick', (e: MouseEvent): void => { this.controller.fit(); e.preventDefault(); }); - this.content.addEventListener('mousedown', (event): void => { + this.canvas.addEventListener('mousedown', (event): void => { if ([0, 1].includes(event.button)) { if ( [Mode.IDLE, Mode.DRAG_CANVAS, Mode.MERGE, Mode.SPLIT] @@ -1146,7 +1164,7 @@ export class CanvasViewImpl implements CanvasView, Listener { window.document.addEventListener('keydown', this.onShiftKeyDown); window.document.addEventListener('keyup', this.onShiftKeyUp); - this.content.addEventListener('wheel', (event): void => { + this.canvas.addEventListener('wheel', (event): void => { if (event.ctrlKey) return; const { offset } = this.controller.geometry; const point = translateToSVG(this.content, [event.clientX, event.clientY]); @@ -1160,7 +1178,7 @@ export class CanvasViewImpl implements CanvasView, Listener { event.preventDefault(); }); - this.content.addEventListener('mousemove', (e): void => { + this.canvas.addEventListener('mousemove', (e): void => { this.controller.drag(e.clientX, e.clientY); if (this.mode !== Mode.IDLE) return; @@ -1388,16 +1406,23 @@ export class CanvasViewImpl implements CanvasView, Listener { } } else if (reason === UpdateReasons.DRAW) { const data: DrawData = this.controller.drawData; - if (data.enabled && this.mode === Mode.IDLE) { + if (data.enabled && [Mode.IDLE, Mode.DRAW].includes(this.mode)) { this.canvas.style.cursor = 'crosshair'; this.mode = Mode.DRAW; if (typeof data.redraw === 'number') { this.setupInnerFlags(data.redraw, 'drawHidden', true); } - this.drawHandler.draw(data, this.geometry); + if (data.shapeType === 'mask') { + this.content.style.pointerEvents = 'none'; + this.masksDrawHandler.draw(data, this.geometry); + } else { + this.drawHandler.draw(data, this.geometry); + } } else { + this.content.style.pointerEvents = ''; this.canvas.style.cursor = ''; if (this.mode !== Mode.IDLE) { + this.masksDrawHandler.draw(data, this.geometry); this.drawHandler.draw(data, this.geometry); } } diff --git a/cvat-canvas/src/typescript/masksDrawHandler.ts b/cvat-canvas/src/typescript/masksDrawHandler.ts new file mode 100644 index 00000000000..2c54acd2cca --- /dev/null +++ b/cvat-canvas/src/typescript/masksDrawHandler.ts @@ -0,0 +1,104 @@ +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: MIT + +import { fabric } from 'fabric'; + +import { DrawData, Geometry } from './canvasModel'; + +export interface MasksDrawHandler { + draw(drawData: DrawData, geometry: Geometry): void; + transform(geometry: Geometry): void; + cancel(): void; +} + +export class MasksDrawHandlerImpl implements MasksDrawHandler { + private onDrawDone: (data: object | null, duration?: number, continueDraw?: boolean) => void; + private isDrawing: boolean; + private drawData: DrawData; + private canvas: fabric.Canvas; + private drawnPaths: fabric.Path[]; + + private release(): void { + this.isDrawing = false; + } + + public constructor( + onDrawDone: (data: object | null, duration?: number, continueDraw?: boolean) => void, + canvas: HTMLCanvasElement, + ) { + this.isDrawing = false; + this.drawData = null; + this.drawnPaths = []; + this.onDrawDone = onDrawDone; + this.canvas = new fabric.Canvas(canvas); + this.canvas.getElement().parentElement.classList.add('cvat_masks_canvas_wrapper'); + this.canvas.imageSmoothingEnabled = false; + this.canvas.on('path:created', (opt) => { + if (this.drawData.brushTool?.type === 'eraser') { + (opt as any).path.globalCompositeOperation = 'destination-out'; + } else { + (opt as any).path.globalCompositeOperation = 'xor'; + } + if (this.drawData.brushTool.type === 'eraser') { + const color = fabric.Color.fromHex('#ffffff'); + color.setAlpha(1); + (opt as any).path.stroke = color.toRgba(); + } + this.drawnPaths.push((opt as any).path); + + let imageData = this.canvas.getContext('2d').getImageData(0, 0, 1920, 1080).data; + let alpha = []; + for (let i = 3; i < 8294400; i += 4) { + alpha.push(imageData[i]); + } + + // todo: finish and save alpha channel to rle string, draw it back on canvas + console.log(alpha) + }); + + this.canvas.on('mouse:up:before', (e) => { + console.log(e); + }) + } + + public transform(geometry: Geometry): void { + const topCanvas = this.canvas.getElement().parentElement as HTMLDivElement; + topCanvas.style.top = `${geometry.top}px`; + topCanvas.style.left = `${geometry.left}px`; + topCanvas.style.transform = `scale(${geometry.scale}) rotate(${geometry.angle}deg)`; + } + + public draw(drawData: DrawData, geometry: Geometry): void { + this.drawData = drawData; + if (drawData.enabled) { + if (!this.isDrawing) { + this.canvas.setHeight(geometry.image.height); + this.canvas.setWidth(geometry.image.width); + this.canvas.isDrawingMode = true; + this.isDrawing = true; + } else { + this.canvas.freeDrawingBrush = new fabric.PencilBrush(this.canvas); + this.canvas.freeDrawingBrush.strokeLineCap = drawData.brushTool.form; + this.canvas.freeDrawingBrush.width = drawData.brushTool.size; + this.canvas.freeDrawingBrush.strokeLineCap = drawData.brushTool.form === 'circle' ? 'round' : 'square'; + this.canvas.freeDrawingBrush.strokeLineJoin = 'round'; + if (drawData.brushTool.type === 'eraser') { + const color = fabric.Color.fromHex('#ffffff'); + color.setAlpha(0.5); + this.canvas.freeDrawingBrush.color = color.toRgba(); + } else { + const color = fabric.Color.fromHex(drawData.brushTool.color); + color.setAlpha(0.5); + this.canvas.freeDrawingBrush.color = color.toRgba(); + } + } + } else { + this.release(); + } + } + + public cancel(): void { + this.release(); + } +} diff --git a/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx b/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx index 39423089891..6da46dd9d73 100644 --- a/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx @@ -6,27 +6,40 @@ import './brush-toolbox-styles.scss'; import React, { useEffect, useState } from 'react'; import ReactDOM from 'react-dom'; -import { useDispatch, useSelector } from 'react-redux'; +import { useSelector } from 'react-redux'; import Button from 'antd/lib/button'; -import Icon, { BgColorsOutlined, CheckOutlined, DragOutlined, PlusOutlined } from '@ant-design/icons'; +import Icon, { + BgColorsOutlined, CheckOutlined, DragOutlined, PlusOutlined, +} from '@ant-design/icons'; import InputNumber from 'antd/lib/input-number'; import Select from 'antd/lib/select'; +import { Canvas } from 'cvat-canvas-wrapper'; +import { BrushIcon, EraserIcon } from 'icons'; import CVATTooltip from 'components/common/cvat-tooltip'; -import { CombinedState } from 'reducers/interfaces'; -import { updateCanvasBrushTools } from 'actions/annotation-actions'; +import { CombinedState, ShapeType } from 'reducers/interfaces'; import LabelSelector from 'components/label-selector/label-selector'; import useDraggable from './draggable-hoc'; -import { BrushIcon, EraserIcon } from 'icons'; -function BrushTools(): React.ReactPortal | null { +const DraggableArea = ( +
+ +
+); + +function BrushTools(): React.ReactPortal { const defaultLabelID = useSelector((state: CombinedState) => state.annotation.drawing.activeLabelID); const config = useSelector((state: CombinedState) => state.annotation.canvas.brushTools); + const canvasInstance = useSelector((state: CombinedState) => state.annotation.canvas.instance); const { visible } = config; const labels = useSelector((state: CombinedState) => state.annotation.job.labels); const [activeLabelID, setActiveLabelID] = useState(null); const [currentTool, setCurrentTool] = useState<'brush' | 'eraser' | 'fill'>('brush'); + const [brushForm, setBrushForm] = useState<'circle' | 'square'>('circle'); const [[top, left], setTopLeft] = useState([0, 0]); + const [brushSize, setBrushSize] = useState(10); + const [fillThreshold, setFillThreshild] = useState(10); + const [removeUnderlyingPixels, setRemoveUnderlyingPixels] = useState(false); const dragBar = useDraggable( (): number[] => { const [element] = window.document.getElementsByClassName('cvat-brush-tools-toolbox'); @@ -38,17 +51,32 @@ function BrushTools(): React.ReactPortal | null { return [0, 0]; }, (newTop, newLeft) => setTopLeft([newTop, newLeft]), - ( -
- -
- ), + DraggableArea, ); useEffect(() => { setActiveLabelID(defaultLabelID); }, [defaultLabelID]); + useEffect(() => { + const label = labels.find((_label: any) => _label.id === activeLabelID); + if (visible && label && canvasInstance instanceof Canvas) { + canvasInstance.draw({ + enabled: true, + shapeType: ShapeType.MASK, + crosshair: false, + brushTool: { + type: currentTool, + size: brushSize, + form: brushForm, + color: label.color, + fillThreshold, + removeUnderlyingPixels, + }, + }); + } + }, [currentTool, brushSize, brushForm, removeUnderlyingPixels, visible, activeLabelID]); + useEffect(() => { const canvasContainer = window.document.getElementsByClassName('cvat-canvas-container')[0]; if (canvasContainer) { @@ -57,12 +85,11 @@ function BrushTools(): React.ReactPortal | null { } }, []); - if (!visible) { - return null; - } - + const MIN_BRUSH_SIZE = 1; + const MIN_FILL_THRESHOLD = 1; + const MAX_FILL_THRESHOLD = 255; return ReactDOM.createPortal(( -
+
+ + + ); +} + function PropagateItem(props: ItemProps): JSX.Element { const { toolProps, ...rest } = props; const { propagateShortcut, propagate } = toolProps; @@ -209,6 +224,7 @@ export default function ItemMenu(props: Props): JSX.Element { TO_FOREGROUND = 'to_foreground', SWITCH_COLOR = 'switch_color', REMOVE_ITEM = 'remove_item', + EDIT_MASK = 'edit_mask', } const is2D = jobInstance.dimension === DimensionType.DIM_2D; @@ -219,6 +235,7 @@ export default function ItemMenu(props: Props): JSX.Element { {!readonly && objectType !== ObjectType.TAG && ( )} + {!readonly && } {!readonly && } {is2D && !readonly && [ShapeType.POLYGON, ShapeType.POLYLINE, ShapeType.CUBOID].includes(shapeType) && ( diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx index a6c58b53da9..dab46d1b802 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx @@ -40,6 +40,7 @@ interface Props { changeLabel(label: any): void; changeColor(color: string): void; resetCuboidPerspective(): void; + edit(): void; } function ObjectItemComponent(props: Props): JSX.Element { @@ -69,6 +70,7 @@ function ObjectItemComponent(props: Props): JSX.Element { changeLabel, changeColor, resetCuboidPerspective, + edit, jobInstance, } = props; @@ -124,6 +126,7 @@ function ObjectItemComponent(props: Props): JSX.Element { toBackground={toBackground} toForeground={toForeground} resetCuboidPerspective={resetCuboidPerspective} + edit={edit} /> {!!attributes.length && ( diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx index 40088f08cb9..51c38bee4da 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx @@ -142,6 +142,14 @@ class ObjectItemContainer extends React.PureComponent { } }; + private edit = (): void => { + const { objectState, readonly, canvasInstance } = this.props; + + if (!readonly && canvasInstance instanceof Canvas) { + canvasInstance.edit({ enabled: true, state: objectState }); + } + } + private remove = (): void => { const { objectState, readonly, removeObject, @@ -342,6 +350,7 @@ class ObjectItemContainer extends React.PureComponent { toForeground={this.toForeground} changeColor={this.changeColor} changeLabel={this.changeLabel} + edit={this.edit} resetCuboidPerspective={() => this.resetCuboidPerspective()} /> ); From b78fe685264362ebd62f00c0f61d20ee64dc5fe1 Mon Sep 17 00:00:00 2001 From: Boris Date: Mon, 26 Sep 2022 21:26:21 +0300 Subject: [PATCH 034/106] Support for different colorization settings --- cvat-canvas/src/typescript/canvasModel.ts | 2 +- cvat-canvas/src/typescript/canvasView.ts | 52 ++++++++++++++----- cvat-canvas/src/typescript/consts.ts | 2 + cvat-canvas/src/typescript/masksHandler.ts | 39 ++++++++------ .../annotation-page/canvas/canvas-wrapper.tsx | 8 --- 5 files changed, 64 insertions(+), 39 deletions(-) diff --git a/cvat-canvas/src/typescript/canvasModel.ts b/cvat-canvas/src/typescript/canvasModel.ts index 5856e384289..de6e0b1b258 100644 --- a/cvat-canvas/src/typescript/canvasModel.ts +++ b/cvat-canvas/src/typescript/canvasModel.ts @@ -65,7 +65,7 @@ export interface Configuration { intelligentPolygonCrop?: boolean; forceFrameUpdate?: boolean; CSSImageFilter?: string; - colorBy?: string; + colorBy?: 'Instance' | 'Group' | 'Label'; selectedShapeOpacity?: number; shapeOpacity?: number; controlPointsSize?: number; diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 4ea4c202722..38dfa7035fc 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -1292,32 +1292,41 @@ export class CanvasViewImpl implements CanvasView, Listener { const { configuration } = model; const updateShapeViews = (states: DrawnState[], parentState?: DrawnState): void => { - for (const state of states) { - const { fill, stroke, 'fill-opacity': fillOpacity } = this.getShapeColorization(state, { configuration, parentState }); - const shapeView = window.document.getElementById(`cvat_canvas_shape_${state.clientID}`); + for (const drawnState of states) { + const { + fill, stroke, 'fill-opacity': fillOpacity, + } = this.getShapeColorization(drawnState, { parentState }); + const shapeView = window.document.getElementById(`cvat_canvas_shape_${drawnState.clientID}`); + const [objectState] = this.controller.objects + .filter((_state: any) => _state.clientID === drawnState.clientID); if (shapeView) { const handler = (shapeView as any).instance.remember('_selectHandler'); if (handler && handler.nested) { handler.nested.fill({ color: fill }); } + if (drawnState.shapeType === 'mask') { + // if there are masks, we need to redraw them + this.deleteObjects([drawnState]); + this.addObjects([objectState]); + continue; + } + (shapeView as any).instance .fill({ color: fill, opacity: fillOpacity }) .stroke({ color: stroke }); } - if (state.elements) { - updateShapeViews(state.elements, state); + if (drawnState.elements) { + updateShapeViews(drawnState.elements, drawnState); } } }; - if (configuration.shapeOpacity !== this.configuration.shapeOpacity || + const withUpdatingShapeViews = configuration.shapeOpacity !== this.configuration.shapeOpacity || configuration.selectedShapeOpacity !== this.configuration.selectedShapeOpacity || configuration.outlinedBorders !== this.configuration.outlinedBorders || - configuration.colorBy !== this.configuration.colorBy) { - updateShapeViews(Object.values(this.drawnStates)); - } + configuration.colorBy !== this.configuration.colorBy; if (configuration.displayAllText && !this.configuration.displayAllText) { for (const i in this.drawnStates) { @@ -1346,6 +1355,10 @@ export class CanvasViewImpl implements CanvasView, Listener { } this.configuration = configuration; + if (withUpdatingShapeViews) { + updateShapeViews(Object.values(this.drawnStates)); + } + if (recreateText) { const states = this.controller.objects; for (const key of Object.keys(this.drawnStates)) { @@ -1821,12 +1834,11 @@ export class CanvasViewImpl implements CanvasView, Listener { } private getShapeColorization(state: any, opts: { - configuration?: Configuration, parentState?: any, } = {}): { fill: string; stroke: string, 'fill-opacity': number } { const { shapeType } = state; const parentShapeType = opts.parentState?.shapeType; - const configuration = opts.configuration || this.configuration; + const { configuration } = this; const { colorBy, shapeOpacity, outlinedBorders } = configuration; let shapeColor = ''; @@ -2115,6 +2127,11 @@ export class CanvasViewImpl implements CanvasView, Listener { shape.removeClass('cvat_canvas_shape_activated'); shape.removeClass('cvat_canvas_shape_draggable'); + if (drawnState.shapeType === 'mask') { + shape.attr('opacity', `${this.configuration.shapeOpacity}`); + } else { + shape.attr('fill-opacity', `${this.configuration.shapeOpacity}`); + } if (!drawnState.pinned) { (shape as any).off('dragstart'); @@ -2200,6 +2217,12 @@ export class CanvasViewImpl implements CanvasView, Listener { } shape.addClass('cvat_canvas_shape_activated'); + if (state.shapeType === 'mask') { + shape.attr('opacity', `${this.configuration.selectedShapeOpacity}`); + } else { + shape.attr('fill-opacity', `${this.configuration.selectedShapeOpacity}`); + } + if (state.shapeType === 'points') { this.content.append(this.svgShapes[clientID].remember('_selectHandler').nested.node); } else { @@ -2387,6 +2410,7 @@ export class CanvasViewImpl implements CanvasView, Listener { (shape as any).on('dblclick', (e: MouseEvent) => { if (e.shiftKey) { this.controller.edit({ enabled: true, state }); + e.stopPropagation(); } }); } @@ -2723,7 +2747,8 @@ export class CanvasViewImpl implements CanvasView, Listener { } private addMask(points: number[], state: any): SVG.Image { - const color = fabric.Color.fromHex(state.color).getSource(); + const colorization = this.getShapeColorization(state); + const color = fabric.Color.fromHex(colorization.fill).getSource(); const [left, top, right, bottom] = points.slice(-4); const imageBitmap = []; for (let i = 0; i < points.length - 4; i++) { @@ -2747,9 +2772,10 @@ export class CanvasViewImpl implements CanvasView, Listener { clientID: state.clientID, 'color-rendering': 'optimizeQuality', id: `cvat_canvas_shape_${state.clientID}`, - fill: state.color, 'shape-rendering': 'geometricprecision', 'data-z-order': state.zOrder, + opacity: colorization['fill-opacity'], + stroke: colorization.stroke, }).addClass('cvat_canvas_shape'); image.move(this.geometry.offset + left, this.geometry.offset + top); image.loaded(() => { diff --git a/cvat-canvas/src/typescript/consts.ts b/cvat-canvas/src/typescript/consts.ts index 96ff980512a..be7cb42a40d 100644 --- a/cvat-canvas/src/typescript/consts.ts +++ b/cvat-canvas/src/typescript/consts.ts @@ -21,6 +21,7 @@ const SNAP_TO_ANGLE_RESIZE_DEFAULT = 0.1; const SNAP_TO_ANGLE_RESIZE_SHIFT = 15; const MINIMUM_TEXT_FONT_SIZE = 8; const SKELETON_RECT_MARGIN = 20; +const MASK_OPACITY_OFFSET = 0.3; const DEFAULT_SHAPE_TEXT_SIZE = 12; const DEFAULT_SHAPE_TEXT_CONTENT = 'id,label,attributes,source,descriptions'; @@ -50,4 +51,5 @@ export default { DEFAULT_UNDEFINED_ATTR_VALUE, MINIMUM_TEXT_FONT_SIZE, SKELETON_RECT_MARGIN, + MASK_OPACITY_OFFSET, }; diff --git a/cvat-canvas/src/typescript/masksHandler.ts b/cvat-canvas/src/typescript/masksHandler.ts index 41a8ad265ba..cf77c54c504 100644 --- a/cvat-canvas/src/typescript/masksHandler.ts +++ b/cvat-canvas/src/typescript/masksHandler.ts @@ -5,15 +5,15 @@ import { fabric } from 'fabric'; import { - Configuration, DrawData, MasksEditData, Geometry, + DrawData, MasksEditData, Geometry, Configuration, } from './canvasModel'; import consts from './consts'; import { PropType, computeWrappingBox } from './shared'; export interface MasksHandler { - configurate(configuration: Configuration): void; draw(drawData: DrawData): void; edit(state: MasksEditData): void; + configurate(configuration: Configuration): void; transform(geometry: Geometry): void; cancel(): void; enabled: boolean; @@ -43,6 +43,7 @@ export class MasksHandlerImpl implements MasksHandler { private editData: MasksEditData | null; + private colorBy: 'Instance' | 'Group' | 'Label'; private latestMousePos: { x: number; y: number; }; private startTimestamp: number; private geometry: Geometry; @@ -106,6 +107,18 @@ export class MasksHandlerImpl implements MasksHandler { this.onEditDone(null, null); } + private getStateColor(state: any): string { + if (this.colorBy === 'Instance') { + return state.color; + } + + if (this.colorBy === 'Label') { + return state.label.color; + } + + return state.group.color; + } + public constructor( onDrawDone: ( data: object | null, @@ -125,6 +138,7 @@ export class MasksHandlerImpl implements MasksHandler { this.drawnObjects = []; this.drawingOpacity = 0.5; this.brushMarker = null; + this.colorBy = 'Instance'; this.onDrawDone = onDrawDone; this.onDrawRepeat = onDrawRepeat; this.onEditDone = onEditDone; @@ -278,6 +292,10 @@ export class MasksHandlerImpl implements MasksHandler { }); } + public configurate(configuration: Configuration) { + this.colorBy = configuration.colorBy; + } + public transform(geometry: Geometry): void { this.geometry = geometry; const { @@ -298,19 +316,6 @@ export class MasksHandlerImpl implements MasksHandler { } } - public configurate(configuration: Configuration): void { - if (typeof configuration.creationOpacity === 'number') { - this.drawingOpacity = Math.max(0, Math.min(1, configuration.creationOpacity)); - - if (this.drawablePolygon) { - this.drawablePolygon.set('opacity', this.drawingOpacity); - this.canvas.renderAll(); - } - - // TODO: can we change opacity for all drawn objects? - } - } - public draw(drawData: DrawData): void { if (drawData.enabled && drawData.shapeType === 'mask' && drawData.brushTool) { this.tool = { ...drawData.brushTool }; @@ -454,7 +459,7 @@ export class MasksHandlerImpl implements MasksHandler { if (editData.enabled && editData.state.shapeType === 'mask') { if (editData.brushTool) { this.tool = { ...editData.brushTool }; - this.tool.color = editData.state.color; + this.tool.color = this.getStateColor(this.editData.state); this.removeBrushMarker(); if (['brush', 'eraser'].includes(this.tool.type)) { @@ -483,7 +488,7 @@ export class MasksHandlerImpl implements MasksHandler { this.canvas.getElement().parentElement.style.display = 'block'; const { points } = editData.state; - const color = fabric.Color.fromHex(editData.state.color).getSource(); + const color = fabric.Color.fromHex(this.getStateColor(editData.state)).getSource(); const [left, top, right, bottom] = points.slice(-4); const imageBitmap = []; for (let i = 0; i < points.length - 4; i++) { diff --git a/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx b/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx index 2c7b61d75f4..008e8f0243f 100644 --- a/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx @@ -244,10 +244,6 @@ export default class CanvasWrapperComponent extends React.PureComponent { if (prevProps.activatedStateID !== null && prevProps.activatedStateID !== activatedStateID) { canvasInstance.activate(null); - const el = window.document.getElementById(`cvat_canvas_shape_${prevProps.activatedStateID}`); - if (el) { - (el as any).instance.fill({ opacity }); - } } if (gridSize !== prevProps.gridSize) { @@ -661,10 +657,6 @@ export default class CanvasWrapperComponent extends React.PureComponent { if (activatedState && activatedState.objectType !== ObjectType.TAG) { canvasInstance.activate(activatedStateID, activatedAttributeID); } - const el = window.document.getElementById(`cvat_canvas_shape_${activatedStateID}`); - if (el) { - ((el as any) as SVGElement).setAttribute('fill-opacity', `${selectedOpacity}`); - } } } From 8cc9ed041aa8e27ab3758772633f83aa7e3479f0 Mon Sep 17 00:00:00 2001 From: Boris Date: Mon, 26 Sep 2022 23:11:36 +0300 Subject: [PATCH 035/106] Implemented bitmap for masks --- cvat-canvas/src/typescript/canvasView.ts | 29 ++++++++++++++++++++++++ cvat-canvas/src/typescript/consts.ts | 2 -- cvat-core/src/object-state.ts | 4 ++-- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 38dfa7035fc..0c921797a56 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -1781,6 +1781,35 @@ export class CanvasViewImpl implements CanvasView, Listener { ctx.fill(); } + if (state.shapeType === 'mask') { + const { points } = state; + const [left, top, right, bottom] = points.splice(-4); + const imageBitmap = []; + for (let i = 0; i < points.length; i++) { + const alpha = points[i]; + imageBitmap.push(alpha * 255, alpha * 255, alpha * 255, alpha * 255); + } + + const canvas = document.createElement('canvas'); + canvas.width = right - left + 1; + canvas.height = bottom - top + 1; + canvas.getContext('2d').putImageData( + new ImageData( + new Uint8ClampedArray(imageBitmap), + canvas.width, + canvas.height, + ), 0, 0, + ); + const dataURL = canvas.toDataURL('image/png'); + const img = document.createElement('img'); + img.addEventListener('load', () => { + ctx.drawImage(img, left, top); + URL.revokeObjectURL(dataURL); + }); + + img.src = dataURL; + } + if (state.shapeType === 'cuboid') { for (let i = 0; i < 5; i++) { const points = [ diff --git a/cvat-canvas/src/typescript/consts.ts b/cvat-canvas/src/typescript/consts.ts index be7cb42a40d..96ff980512a 100644 --- a/cvat-canvas/src/typescript/consts.ts +++ b/cvat-canvas/src/typescript/consts.ts @@ -21,7 +21,6 @@ const SNAP_TO_ANGLE_RESIZE_DEFAULT = 0.1; const SNAP_TO_ANGLE_RESIZE_SHIFT = 15; const MINIMUM_TEXT_FONT_SIZE = 8; const SKELETON_RECT_MARGIN = 20; -const MASK_OPACITY_OFFSET = 0.3; const DEFAULT_SHAPE_TEXT_SIZE = 12; const DEFAULT_SHAPE_TEXT_CONTENT = 'id,label,attributes,source,descriptions'; @@ -51,5 +50,4 @@ export default { DEFAULT_UNDEFINED_ATTR_VALUE, MINIMUM_TEXT_FONT_SIZE, SKELETON_RECT_MARGIN, - MASK_OPACITY_OFFSET, }; diff --git a/cvat-core/src/object-state.ts b/cvat-core/src/object-state.ts index 329a7e76dba..a83441af43c 100644 --- a/cvat-core/src/object-state.ts +++ b/cvat-core/src/object-state.ts @@ -333,7 +333,7 @@ export default class ObjectState { } if (Array.isArray(data.points)) { - return [...data.points]; + return Array.from(data.points); } return []; @@ -365,7 +365,7 @@ export default class ObjectState { data.updateFlags.points = true; } - data.points = [...points]; + data.points = Array.from(points); }, }, rotation: { From 024b7882229cc4ed9124840aad29dacc15e24e3d Mon Sep 17 00:00:00 2001 From: Boris Date: Mon, 26 Sep 2022 23:21:07 +0300 Subject: [PATCH 036/106] Minor performance optimizations --- cvat-canvas/src/typescript/masksHandler.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cvat-canvas/src/typescript/masksHandler.ts b/cvat-canvas/src/typescript/masksHandler.ts index cf77c54c504..a5467ffc7b9 100644 --- a/cvat-canvas/src/typescript/masksHandler.ts +++ b/cvat-canvas/src/typescript/masksHandler.ts @@ -292,7 +292,7 @@ export class MasksHandlerImpl implements MasksHandler { }); } - public configurate(configuration: Configuration) { + public configurate(configuration: Configuration): void { this.colorBy = configuration.colorBy; } @@ -303,9 +303,12 @@ export class MasksHandlerImpl implements MasksHandler { } = geometry; const topCanvas = this.canvas.getElement().parentElement as HTMLDivElement; - this.canvas.setHeight(height); - this.canvas.setWidth(width); - this.canvas.setDimensions({ width, height }); + if (this.canvas.width !== width || this.canvas.height !== height) { + this.canvas.setHeight(height); + this.canvas.setWidth(width); + this.canvas.setDimensions({ width, height }); + } + topCanvas.style.top = `${top}px`; topCanvas.style.left = `${left}px`; topCanvas.style.transform = `scale(${scale}) rotate(${angle}deg)`; From 3e2c611b68faf664b5170486a3a247eab6c28a02 Mon Sep 17 00:00:00 2001 From: Boris Date: Mon, 26 Sep 2022 23:41:38 +0300 Subject: [PATCH 037/106] Updated license headers --- cvat-canvas/package.json | 4 +--- cvat-canvas/src/scss/canvas.scss | 2 +- cvat-canvas/src/typescript/canvas.ts | 1 + cvat-canvas/src/typescript/canvasController.ts | 1 + cvat-canvas/src/typescript/canvasModel.ts | 1 + cvat-canvas/src/typescript/editHandler.ts | 1 + cvat-core/src/annotations-collection.ts | 2 +- cvat-core/src/annotations-objects.ts | 2 +- cvat-core/src/api-implementation.ts | 1 + cvat-core/src/api.ts | 1 + cvat-core/src/exceptions.ts | 1 + cvat-core/src/frames.ts | 1 + cvat-core/src/lambda-manager.ts | 1 + cvat-core/src/object-state.ts | 1 + cvat-core/src/statistics.ts | 1 + .../annotation-page/canvas/brush-toolbox-styles.scss | 2 +- cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx | 2 +- .../components/annotation-page/canvas/canvas-context-menu.tsx | 1 + .../src/components/annotation-page/canvas/canvas-wrapper.tsx | 1 + .../src/components/annotation-page/canvas/draggable-hoc.tsx | 1 + .../controls-side-bar/control-visibility-observer.tsx | 1 + .../controls-side-bar/controls-side-bar.tsx | 1 + .../controls-side-bar/draw-mask-control.tsx | 2 +- .../controls-side-bar/draw-mask-popover.tsx | 2 +- .../objects-side-bar/object-item-basics.tsx | 1 + .../standard-workspace/objects-side-bar/object-item-menu.tsx | 1 + .../standard-workspace/objects-side-bar/object-item.tsx | 1 + .../standard-workspace/objects-side-bar/styles.scss | 1 + .../containers/annotation-page/canvas/canvas-context-menu.tsx | 1 + .../controls-side-bar/draw-mask-popover.tsx | 2 +- .../standard-workspace/objects-side-bar/object-item.tsx | 1 + 31 files changed, 31 insertions(+), 11 deletions(-) diff --git a/cvat-canvas/package.json b/cvat-canvas/package.json index 0bc97aba656..2e931876d18 100644 --- a/cvat-canvas/package.json +++ b/cvat-canvas/package.json @@ -17,6 +17,7 @@ ], "dependencies": { "@types/polylabel": "^1.0.5", + "@types/fabric": "^4.5.7", "fabric": "^5.2.1", "polylabel": "^1.1.0", "svg.draggable.js": "2.2.2", @@ -24,8 +25,5 @@ "svg.js": "2.7.1", "svg.resize.js": "1.4.3", "svg.select.js": "3.0.1" - }, - "devDependencies": { - "@types/fabric": "^4.5.7" } } diff --git a/cvat-canvas/src/scss/canvas.scss b/cvat-canvas/src/scss/canvas.scss index 7b25971f5f3..c0299e1c64f 100644 --- a/cvat-canvas/src/scss/canvas.scss +++ b/cvat-canvas/src/scss/canvas.scss @@ -1,5 +1,5 @@ // Copyright (C) 2020-2022 Intel Corporation -// Copyright (C) 2022 CVAT.ai Corp +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-canvas/src/typescript/canvas.ts b/cvat-canvas/src/typescript/canvas.ts index f92820f0b47..9b4c6c60d11 100644 --- a/cvat-canvas/src/typescript/canvas.ts +++ b/cvat-canvas/src/typescript/canvas.ts @@ -1,4 +1,5 @@ // Copyright (C) 2019-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-canvas/src/typescript/canvasController.ts b/cvat-canvas/src/typescript/canvasController.ts index bfc7186edc0..d8b50fc05db 100644 --- a/cvat-canvas/src/typescript/canvasController.ts +++ b/cvat-canvas/src/typescript/canvasController.ts @@ -1,4 +1,5 @@ // Copyright (C) 2019-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-canvas/src/typescript/canvasModel.ts b/cvat-canvas/src/typescript/canvasModel.ts index de6e0b1b258..adb2391aa09 100644 --- a/cvat-canvas/src/typescript/canvasModel.ts +++ b/cvat-canvas/src/typescript/canvasModel.ts @@ -1,4 +1,5 @@ // Copyright (C) 2019-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-canvas/src/typescript/editHandler.ts b/cvat-canvas/src/typescript/editHandler.ts index b980f11484e..1e907ba1c3a 100644 --- a/cvat-canvas/src/typescript/editHandler.ts +++ b/cvat-canvas/src/typescript/editHandler.ts @@ -1,4 +1,5 @@ // Copyright (C) 2019-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-core/src/annotations-collection.ts b/cvat-core/src/annotations-collection.ts index 9ed0d5f81fa..4e3a02df83d 100644 --- a/cvat-core/src/annotations-collection.ts +++ b/cvat-core/src/annotations-collection.ts @@ -1,5 +1,5 @@ // Copyright (C) 2019-2022 Intel Corporation -// Copyright (C) 2022 CVAT.ai Corp +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-core/src/annotations-objects.ts b/cvat-core/src/annotations-objects.ts index 18009fec2ea..cd73d8ba0d1 100644 --- a/cvat-core/src/annotations-objects.ts +++ b/cvat-core/src/annotations-objects.ts @@ -1,5 +1,5 @@ // Copyright (C) 2019-2022 Intel Corporation -// Copyright (C) 2022 CVAT.ai Corp +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-core/src/api-implementation.ts b/cvat-core/src/api-implementation.ts index a7ee5439856..670101a5026 100644 --- a/cvat-core/src/api-implementation.ts +++ b/cvat-core/src/api-implementation.ts @@ -1,4 +1,5 @@ // Copyright (C) 2019-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-core/src/api.ts b/cvat-core/src/api.ts index 767422d07bb..54b29731748 100644 --- a/cvat-core/src/api.ts +++ b/cvat-core/src/api.ts @@ -1,4 +1,5 @@ // Copyright (C) 2019-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-core/src/exceptions.ts b/cvat-core/src/exceptions.ts index 32d2a913bea..ba40b0a8e6f 100644 --- a/cvat-core/src/exceptions.ts +++ b/cvat-core/src/exceptions.ts @@ -1,4 +1,5 @@ // Copyright (C) 2019-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-core/src/frames.ts b/cvat-core/src/frames.ts index e51141e9d5f..0a4f6954063 100644 --- a/cvat-core/src/frames.ts +++ b/cvat-core/src/frames.ts @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-core/src/lambda-manager.ts b/cvat-core/src/lambda-manager.ts index 67ff76cc29a..184723ab08f 100644 --- a/cvat-core/src/lambda-manager.ts +++ b/cvat-core/src/lambda-manager.ts @@ -1,4 +1,5 @@ // Copyright (C) 2019-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-core/src/object-state.ts b/cvat-core/src/object-state.ts index a83441af43c..8656da5bbdb 100644 --- a/cvat-core/src/object-state.ts +++ b/cvat-core/src/object-state.ts @@ -1,4 +1,5 @@ // Copyright (C) 2019-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-core/src/statistics.ts b/cvat-core/src/statistics.ts index 7229b376e9f..8b7f7a8a27c 100644 --- a/cvat-core/src/statistics.ts +++ b/cvat-core/src/statistics.ts @@ -1,4 +1,5 @@ // Copyright (C) 2019-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/components/annotation-page/canvas/brush-toolbox-styles.scss b/cvat-ui/src/components/annotation-page/canvas/brush-toolbox-styles.scss index b41bb68ced2..d0331e82474 100644 --- a/cvat-ui/src/components/annotation-page/canvas/brush-toolbox-styles.scss +++ b/cvat-ui/src/components/annotation-page/canvas/brush-toolbox-styles.scss @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx b/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx index c897fc90171..6ba99ac595b 100644 --- a/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/components/annotation-page/canvas/canvas-context-menu.tsx b/cvat-ui/src/components/annotation-page/canvas/canvas-context-menu.tsx index 0ca596afe3e..3802077d7ad 100644 --- a/cvat-ui/src/components/annotation-page/canvas/canvas-context-menu.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/canvas-context-menu.tsx @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx b/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx index 008e8f0243f..dc06fd9e0f8 100644 --- a/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx @@ -1,4 +1,5 @@ // Copyright (C) 2020-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/components/annotation-page/canvas/draggable-hoc.tsx b/cvat-ui/src/components/annotation-page/canvas/draggable-hoc.tsx index 30bfda9a2e5..59cc85a4714 100644 --- a/cvat-ui/src/components/annotation-page/canvas/draggable-hoc.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/draggable-hoc.tsx @@ -1,4 +1,5 @@ // Copyright (C) 2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/control-visibility-observer.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/control-visibility-observer.tsx index e139447d6e1..28f96b44e26 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/control-visibility-observer.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/control-visibility-observer.tsx @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx index ccb9757f6eb..b03b41f479f 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx @@ -1,4 +1,5 @@ // Copyright (C) 2020-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-mask-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-mask-control.tsx index 2ef200b6709..1fba8150f5c 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-mask-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-mask-control.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover.tsx index 615ad5c043b..504f236fc3f 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item-basics.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item-basics.tsx index 2c1a4448b55..12c63eadd5c 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item-basics.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item-basics.tsx @@ -1,4 +1,5 @@ // Copyright (C) 2020-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item-menu.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item-menu.tsx index 67440a46d58..4a8eca564c6 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item-menu.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item-menu.tsx @@ -1,4 +1,5 @@ // Copyright (C) 2020-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx index dab46d1b802..b5909e8de46 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss index 189602e66c0..641038144f4 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss @@ -1,4 +1,5 @@ // Copyright (C) 2020-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/containers/annotation-page/canvas/canvas-context-menu.tsx b/cvat-ui/src/containers/annotation-page/canvas/canvas-context-menu.tsx index 21eba4ed8b1..9b27e268f00 100644 --- a/cvat-ui/src/containers/annotation-page/canvas/canvas-context-menu.tsx +++ b/cvat-ui/src/containers/annotation-page/canvas/canvas-context-menu.tsx @@ -1,4 +1,5 @@ // Copyright (C) 2020-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover.tsx index 84208db10da..6fe25bf9b4f 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/draw-mask-popover.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx index 51c38bee4da..f665369e243 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT From bdb81877ddb3e980f3065972829a48a2a8d5a80a Mon Sep 17 00:00:00 2001 From: Boris Date: Tue, 27 Sep 2022 10:45:15 +0300 Subject: [PATCH 038/106] Added statistics for masks --- cvat-core/src/annotations-collection.ts | 4 +- cvat-core/src/api.ts | 2 +- cvat-core/src/statistics.ts | 153 +++++------------- .../top-bar/statistics-modal.tsx | 10 +- 4 files changed, 49 insertions(+), 120 deletions(-) diff --git a/cvat-core/src/annotations-collection.ts b/cvat-core/src/annotations-collection.ts index 4e3a02df83d..573c4ca7c5e 100644 --- a/cvat-core/src/annotations-collection.ts +++ b/cvat-core/src/annotations-collection.ts @@ -13,7 +13,7 @@ } = require('./annotations-objects'); const AnnotationsFilter = require('./annotations-filter').default; const { checkObjectType } = require('./common'); - const Statistics = require('./statistics'); + const Statistics = require('./statistics').default; const { Label } = require('./labels'); const { ArgumentError, ScriptingError } = require('./exceptions'); const ObjectState = require('./object-state').default; @@ -598,7 +598,7 @@ statistics() { const labels = {}; - const shapes = ['rectangle', 'polygon', 'polyline', 'points', 'ellipse', 'cuboid', 'skeleton', 'mask']; + const shapes = ['rectangle', 'polygon', 'polyline', 'points', 'ellipse', 'cuboid', 'skeleton']; const body = { ...(shapes.reduce((acc, val) => ({ ...acc, diff --git a/cvat-core/src/api.ts b/cvat-core/src/api.ts index 54b29731748..87360912b57 100644 --- a/cvat-core/src/api.ts +++ b/cvat-core/src/api.ts @@ -13,7 +13,7 @@ function build() { const loggerStorage = require('./logger-storage'); const Log = require('./log'); const ObjectState = require('./object-state').default; - const Statistics = require('./statistics'); + const Statistics = require('./statistics').default; const Comment = require('./comment'); const Issue = require('./issue'); const { Job, Task } = require('./session'); diff --git a/cvat-core/src/statistics.ts b/cvat-core/src/statistics.ts index 8b7f7a8a27c..d30baf83dc8 100644 --- a/cvat-core/src/statistics.ts +++ b/cvat-core/src/statistics.ts @@ -3,121 +3,42 @@ // // SPDX-License-Identifier: MIT -(() => { - /** - * Class representing collection statistics - * @memberof module:API.cvat.classes - * @hideconstructor - */ - class Statistics { - constructor(label, total) { - Object.defineProperties( - this, - Object.freeze({ - /** - * Statistics collected by labels, has the following structure: - * @example - * { - * label: { - * rectangle: { - * track: 10, - * shape: 11, - * }, - * polygon: { - * track: 13, - * shape: 14, - * }, - * polyline: { - * track: 16, - * shape: 17, - * }, - * points: { - * track: 19, - * shape: 20, - * }, - * ellipse: { - * track: 13, - * shape: 15, - * }, - * cuboid: { - * tracks: 21, - * shapes: 22, - * }, - * skeleton: { - * track: 21, - * shape: 22, - * }, - * mask: { - * shapes: 22, - * }, - * tag: 66, - * manually: 207, - * interpolated: 500, - * total: 630, - * } - * } - * @name label - * @type {Object} - * @memberof module:API.cvat.classes.Statistics - * @readonly - * @instance - */ - label: { - get: () => JSON.parse(JSON.stringify(label)), - }, - /** - * Total objects statistics (within all the labels), has the following structure: - * @example - * { - * rectangle: { - * tracks: 10, - * shapes: 11, - * }, - * polygon: { - * tracks: 13, - * shapes: 14, - * }, - * polyline: { - * tracks: 16, - * shapes: 17, - * }, - * point: { - * tracks: 19, - * shapes: 20, - * }, - * ellipse: { - * tracks: 13, - * shapes: 15, - * }, - * cuboid: { - * tracks: 21, - * shapes: 22, - * }, - * skeleton: { - * tracks: 21, - * shapes: 22, - * }, - * mask: { - * shapes: 22, - * }, - * tag: 66, - * manually: 186, - * interpolated: 500, - * total: 608, - * } - * @name total - * @type {Object} - * @memberof module:API.cvat.classes.Statistics - * @readonly - * @instance - */ - total: { - get: () => JSON.parse(JSON.stringify(total)), - }, - }), - ); - } +interface ObjectStatistics { + track: number; + shape: number; +} + +interface StatisticsBody { + rectangle: ObjectStatistics; + polygon: ObjectStatistics; + polyline: ObjectStatistics; + points: ObjectStatistics; + ellipse: ObjectStatistics; + cuboid: ObjectStatistics; + skeleton: ObjectStatistics; + mask: { + shape: number; + }; + tag: number; + manually: number; + interpolated: number; + total: number; +} + +export default class Statistics { + private labelData: Record; + private totalData: StatisticsBody; + + constructor(label: Statistics['labelData'], total: Statistics['totalData']) { + this.labelData = label; + this.totalData = total; } - module.exports = Statistics; -})(); + public get label(): Record { + return JSON.parse(JSON.stringify(this.labelData)); + } + + public get total(): StatisticsBody { + return JSON.parse(JSON.stringify(this.totalData)); + } +} diff --git a/cvat-ui/src/components/annotation-page/top-bar/statistics-modal.tsx b/cvat-ui/src/components/annotation-page/top-bar/statistics-modal.tsx index ec59c114d99..efd418f4f33 100644 --- a/cvat-ui/src/components/annotation-page/top-bar/statistics-modal.tsx +++ b/cvat-ui/src/components/annotation-page/top-bar/statistics-modal.tsx @@ -114,6 +114,7 @@ function StatisticsModalComponent(props: StateToProps & DispatchToProps): JSX.El ellipse: `${data.label[key].ellipse.shape} / ${data.label[key].ellipse.track}`, cuboid: `${data.label[key].cuboid.shape} / ${data.label[key].cuboid.track}`, skeleton: `${data.label[key].skeleton.shape} / ${data.label[key].skeleton.track}`, + mask: `${data.label[key].mask.shape}`, tag: data.label[key].tag, manually: data.label[key].manually, interpolated: data.label[key].interpolated, @@ -130,6 +131,7 @@ function StatisticsModalComponent(props: StateToProps & DispatchToProps): JSX.El ellipse: `${data.total.ellipse.shape} / ${data.total.ellipse.track}`, cuboid: `${data.total.cuboid.shape} / ${data.total.cuboid.track}`, skeleton: `${data.total.skeleton.shape} / ${data.total.skeleton.track}`, + mask: `${data.total.mask.shape}`, tag: data.total.tag, manually: data.total.manually, interpolated: data.total.interpolated, @@ -137,7 +139,7 @@ function StatisticsModalComponent(props: StateToProps & DispatchToProps): JSX.El }); const makeShapesTracksTitle = (title: string): JSX.Element => ( - + {title} @@ -210,6 +212,12 @@ function StatisticsModalComponent(props: StateToProps & DispatchToProps): JSX.El key: 'skeleton', width: 100, }, + { + title: makeShapesTracksTitle('Mask'), + dataIndex: 'mask', + key: 'mask', + width: 100, + }, { title: Tag , dataIndex: 'tag', From 57b0b0ae44ebe968299dbbc88a50ae6be56a9e65 Mon Sep 17 00:00:00 2001 From: Boris Date: Tue, 27 Sep 2022 18:31:30 +0300 Subject: [PATCH 039/106] Removed fill tool (useless) --- cvat-canvas/src/typescript/canvasModel.ts | 3 +- .../annotation-page/canvas/brush-tools.tsx | 40 ++----------------- 2 files changed, 4 insertions(+), 39 deletions(-) diff --git a/cvat-canvas/src/typescript/canvasModel.ts b/cvat-canvas/src/typescript/canvasModel.ts index adb2391aa09..53c3db29ba3 100644 --- a/cvat-canvas/src/typescript/canvasModel.ts +++ b/cvat-canvas/src/typescript/canvasModel.ts @@ -74,11 +74,10 @@ export interface Configuration { } export interface BrushTool { - type: 'brush' | 'eraser' | 'fill' | 'polygon-plus' | 'polygon-minus'; + type: 'brush' | 'eraser' | 'polygon-plus' | 'polygon-minus'; color: string; removeUnderlyingPixels: boolean; form: 'circle' | 'square'; - fillThreshold: number; size: number; } diff --git a/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx b/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx index 6ba99ac595b..df7bba23c03 100644 --- a/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx @@ -9,7 +9,7 @@ import ReactDOM from 'react-dom'; import { useDispatch, useSelector } from 'react-redux'; import Button from 'antd/lib/button'; import Icon, { - BgColorsOutlined, CheckOutlined, DragOutlined, PlusOutlined, VerticalAlignBottomOutlined, + CheckOutlined, DragOutlined, PlusOutlined, VerticalAlignBottomOutlined, } from '@ant-design/icons'; import InputNumber from 'antd/lib/input-number'; import Select from 'antd/lib/select'; @@ -41,11 +41,10 @@ function BrushTools(): React.ReactPortal { const [activeLabelID, setActiveLabelID] = useState(null); const [editableState, setEditableState] = useState(null); - const [currentTool, setCurrentTool] = useState<'brush' | 'eraser' | 'fill' | 'polygon-plus' | 'polygon-minus'>('brush'); + const [currentTool, setCurrentTool] = useState<'brush' | 'eraser' | 'polygon-plus' | 'polygon-minus'>('brush'); const [brushForm, setBrushForm] = useState<'circle' | 'square'>('circle'); const [[top, left], setTopLeft] = useState([0, 0]); const [brushSize, setBrushSize] = useState(10); - const [fillThreshold, setFillThreshild] = useState(10); const [removeUnderlyingPixels, setRemoveUnderlyingPixels] = useState(false); const dragBar = useDraggable( @@ -80,7 +79,6 @@ function BrushTools(): React.ReactPortal { size: brushSize, form: brushForm, color: label.color, - fillThreshold, removeUnderlyingPixels, }, }); @@ -93,7 +91,6 @@ function BrushTools(): React.ReactPortal { size: brushSize, form: brushForm, color: label.color, - fillThreshold, removeUnderlyingPixels, }, }); @@ -157,8 +154,6 @@ function BrushTools(): React.ReactPortal { }, [visible]); const MIN_BRUSH_SIZE = 1; - const MIN_FILL_THRESHOLD = 1; - const MAX_FILL_THRESHOLD = 255; return ReactDOM.createPortal((
), window.document.body); From 801e95acd440c781adef580349094b857e2c31eb Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Mon, 24 Oct 2022 02:47:14 -0700 Subject: [PATCH 103/106] Minor refactoring --- cvat-canvas/src/typescript/masksHandler.ts | 18 ++++++------- .../annotation-page/canvas/brush-tools.tsx | 4 ++- cvat-ui/src/reducers/annotation-reducer.ts | 26 +++++++++---------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/cvat-canvas/src/typescript/masksHandler.ts b/cvat-canvas/src/typescript/masksHandler.ts index fe956f5ca18..32f47c08059 100644 --- a/cvat-canvas/src/typescript/masksHandler.ts +++ b/cvat-canvas/src/typescript/masksHandler.ts @@ -50,7 +50,7 @@ export class MasksHandlerImpl implements MasksHandler { private resizeBrushToolLatestX: number; private brushMarker: fabric.Rect | fabric.Circle | null; private drawablePolygon: null | fabric.Polygon; - private polygonIsBeingDrawn: boolean; + private isPolygonDrawing: boolean; private drawnObjects: (fabric.Polygon | fabric.Circle | fabric.Rect | fabric.Line | fabric.Image)[]; private tool: DrawData['brushTool'] | null; @@ -69,7 +69,7 @@ export class MasksHandlerImpl implements MasksHandler { const canvasWrapper = this.canvas.getElement().parentElement; canvasWrapper.style.pointerEvents = ''; canvasWrapper.style.zIndex = ''; - this.polygonIsBeingDrawn = false; + this.isPolygonDrawing = false; this.vectorDrawHandler.draw({ enabled: false }, this.geometry); } @@ -127,8 +127,8 @@ export class MasksHandlerImpl implements MasksHandler { private releaseDraw(): void { this.removeBrushMarker(); this.releaseCanvasWrapperCSS(); - if (this.polygonIsBeingDrawn) { - this.polygonIsBeingDrawn = false; + if (this.isPolygonDrawing) { + this.isPolygonDrawing = false; this.vectorDrawHandler.cancel(); } this.canvas.clear(); @@ -143,8 +143,8 @@ export class MasksHandlerImpl implements MasksHandler { private releaseEdit(): void { this.removeBrushMarker(); this.releaseCanvasWrapperCSS(); - if (this.polygonIsBeingDrawn) { - this.polygonIsBeingDrawn = false; + if (this.isPolygonDrawing) { + this.isPolygonDrawing = false; this.vectorDrawHandler.cancel(); } this.canvas.clear(); @@ -224,7 +224,7 @@ export class MasksHandlerImpl implements MasksHandler { } private updateBrushTools(brushTool?: BrushTool, opts: Partial = {}): void { - if (this.polygonIsBeingDrawn) { + if (this.isPolygonDrawing) { // tool was switched from polygon to brush for example this.keepDrawnPolygon(); } @@ -254,7 +254,7 @@ export class MasksHandlerImpl implements MasksHandler { } if (this.tool?.type?.startsWith('polygon-')) { - this.polygonIsBeingDrawn = true; + this.isPolygonDrawing = true; this.vectorDrawHandler.draw({ enabled: true, shapeType: 'polygon', @@ -308,7 +308,7 @@ export class MasksHandlerImpl implements MasksHandler { this.isEditing = false; this.isMouseDown = false; this.isBrushSizeChanging = false; - this.polygonIsBeingDrawn = false; + this.isPolygonDrawing = false; this.drawData = null; this.editData = null; this.drawnObjects = []; diff --git a/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx b/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx index 3901ff1ce0f..dfcaf472db4 100644 --- a/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx @@ -258,7 +258,9 @@ function BrushTools(): React.ReactPortal | null { value={defaultLabelID} onChange={({ id: labelID }: { id: number }) => { if (Number.isInteger(labelID)) { - dispatch(rememberObject({ activeLabelID: labelID })); + dispatch( + rememberObject({ activeLabelID: labelID }), + ); } }} /> diff --git a/cvat-ui/src/reducers/annotation-reducer.ts b/cvat-ui/src/reducers/annotation-reducer.ts index e30119321f8..15bd9d55fd1 100644 --- a/cvat-ui/src/reducers/annotation-reducer.ts +++ b/cvat-ui/src/reducers/annotation-reducer.ts @@ -489,20 +489,20 @@ export default (state = defaultState, action: AnyAction): AnnotationState => { const { payload } = action; let { activeControl } = state.canvas; - const controlMapping = { - [ShapeType.RECTANGLE]: ActiveControl.DRAW_RECTANGLE, - [ShapeType.POLYGON]: ActiveControl.DRAW_POLYGON, - [ShapeType.POLYLINE]: ActiveControl.DRAW_POLYLINE, - [ShapeType.POINTS]: ActiveControl.DRAW_POINTS, - [ShapeType.ELLIPSE]: ActiveControl.DRAW_ELLIPSE, - [ShapeType.CUBOID]: ActiveControl.DRAW_CUBOID, - [ShapeType.SKELETON]: ActiveControl.DRAW_SKELETON, - [ShapeType.MASK]: ActiveControl.DRAW_MASK, - }; - - if (payload.activeObjectType === ObjectType.TAG) { + if ('activeObjectType' in payload && payload.activeObjectType === ObjectType.TAG) { activeControl = ActiveControl.CURSOR; - } else { + } else if ('activeShapeType' in payload) { + const controlMapping = { + [ShapeType.RECTANGLE]: ActiveControl.DRAW_RECTANGLE, + [ShapeType.POLYGON]: ActiveControl.DRAW_POLYGON, + [ShapeType.POLYLINE]: ActiveControl.DRAW_POLYLINE, + [ShapeType.POINTS]: ActiveControl.DRAW_POINTS, + [ShapeType.ELLIPSE]: ActiveControl.DRAW_ELLIPSE, + [ShapeType.CUBOID]: ActiveControl.DRAW_CUBOID, + [ShapeType.SKELETON]: ActiveControl.DRAW_SKELETON, + [ShapeType.MASK]: ActiveControl.DRAW_MASK, + }; + activeControl = controlMapping[payload.activeShapeType as ShapeType]; } From 201901bd66ff34600f4b9f64efcd7880856684a8 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Mon, 24 Oct 2022 05:33:43 -0700 Subject: [PATCH 104/106] stopping drawing a polygon using double N --- cvat-canvas/src/typescript/canvasModel.ts | 34 ++++++++++++++++- cvat-canvas/src/typescript/masksHandler.ts | 2 +- .../annotation-page/canvas/brush-tools.tsx | 38 ++++++++++--------- 3 files changed, 54 insertions(+), 20 deletions(-) diff --git a/cvat-canvas/src/typescript/canvasModel.ts b/cvat-canvas/src/typescript/canvasModel.ts index 24aa769b399..ed8a7d5a1c0 100644 --- a/cvat-canvas/src/typescript/canvasModel.ts +++ b/cvat-canvas/src/typescript/canvasModel.ts @@ -275,6 +275,29 @@ const defaultData = { }, }; +function hasShapeIsBeingDrawn(): boolean { + const [element] = window.document.getElementsByClassName('cvat_canvas_shape_drawing'); + if (element) { + return !!(element as any).instance.remember('_paintHandler'); + } + + return false; +} + +function disableInternalSVGDrawing(data: DrawData | MasksEditData, currentData: DrawData | MasksEditData): boolean { + // P.S. spaghetti code, but probably significant refactoring needed to find a better solution + // when it is a mask drawing/editing using polygon fill + // a user needs to close drawing/editing twice + // first close stops internal drawing/editing with svg.js + // the second one stops drawing/editing mask itself + + return !data.enabled && currentData.enabled && + (('shapeType' in currentData && currentData.shapeType === 'mask') || + ('state' in currentData && currentData.state.shapeType === 'mask')) && + currentData.brushTool?.type?.startsWith('polygon-') && + hasShapeIsBeingDrawn(); +} + export class CanvasModelImpl extends MasterImpl implements CanvasModel { private data: { activeElement: ActiveElement; @@ -618,10 +641,16 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { return; } } else { - this.data.drawData = { ...drawData }; if (this.data.drawData.initialState) { this.data.drawData.shapeType = this.data.drawData.initialState.shapeType; } + + if (disableInternalSVGDrawing(drawData, this.data.drawData)) { + this.notify(UpdateReasons.DRAW); + return; + } + + this.data.drawData = { ...drawData }; } // install default values for drawing method @@ -654,6 +683,9 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { if (editData.enabled) { this.data.editData = { ...editData }; + } else if (disableInternalSVGDrawing(editData, this.data.editData)) { + this.notify(UpdateReasons.EDIT); + return; } else { this.data.editData = { enabled: false }; } diff --git a/cvat-canvas/src/typescript/masksHandler.ts b/cvat-canvas/src/typescript/masksHandler.ts index 32f47c08059..e0512c06664 100644 --- a/cvat-canvas/src/typescript/masksHandler.ts +++ b/cvat-canvas/src/typescript/masksHandler.ts @@ -238,7 +238,7 @@ export class MasksHandlerImpl implements MasksHandler { const alpha = +object.stroke.split(',')[3].slice(0, -1); color.setAlpha(alpha); object.set({ stroke: color.toRgba() }); - } else { + } else if (!(object instanceof fabric.Image)) { const alpha = +(object.fill as string).split(',')[3].slice(0, -1); color.setAlpha(alpha); object.set({ fill: color.toRgba() }); diff --git a/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx b/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx index dfcaf472db4..cee63311571 100644 --- a/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/brush-tools.tsx @@ -174,25 +174,27 @@ function BrushTools(): React.ReactPortal | null { } }} /> -