// Copyright (C) 2019-2022 Intel Corporation // Copyright (C) 2023 CVAT.ai Corporation // // SPDX-License-Identifier: MIT import { AttrInputType, LabelType, SerializedAttribute, SerializedLabel, } from 'server-response-types'; import { ShapeType, AttributeType } from './enums'; import { ArgumentError } from './exceptions'; export class Attribute { public id?: number; public defaultValue: string; public inputType: AttrInputType; public mutable: boolean; public name: string; public values: string[]; constructor(initialData: SerializedAttribute) { const data = { id: undefined, default_value: undefined, input_type: undefined, mutable: undefined, name: undefined, values: undefined, }; for (const key in data) { if (Object.prototype.hasOwnProperty.call(data, key)) { if (Object.prototype.hasOwnProperty.call(initialData, key)) { if (Array.isArray(initialData[key])) { data[key] = [...initialData[key]]; } else { data[key] = initialData[key]; } } } } if (!Object.values(AttributeType).includes(data.input_type)) { throw new ArgumentError(`Got invalid attribute type ${data.input_type}`); } Object.defineProperties( this, Object.freeze({ id: { get: () => data.id, }, defaultValue: { get: () => data.default_value, }, inputType: { get: () => data.input_type, }, mutable: { get: () => data.mutable, }, name: { get: () => data.name, }, values: { get: () => [...data.values], }, }), ); } toJSON(): SerializedAttribute { const object: SerializedAttribute = { name: this.name, mutable: this.mutable, input_type: this.inputType, default_value: this.defaultValue, values: this.values, }; if (typeof this.id !== 'undefined') { object.id = this.id; } return object; } } export class Label { public name: string; public readonly id?: number; public readonly color?: string; public readonly attributes: Attribute[]; public readonly type: LabelType; public structure: { sublabels: Label[]; svg: string; } | null; public deleted: boolean; public patched: boolean; public readonly hasParent?: boolean; constructor(initialData: SerializedLabel) { const data = { id: undefined, name: undefined, color: undefined, type: undefined, structure: undefined, has_parent: false, deleted: false, patched: false, svg: undefined, elements: undefined, sublabels: undefined, attributes: [], }; for (const key of Object.keys(data)) { if (Object.prototype.hasOwnProperty.call(initialData, key)) { data[key] = initialData[key]; } } data.attributes = []; if ( Object.prototype.hasOwnProperty.call(initialData, 'attributes') && Array.isArray(initialData.attributes) ) { for (const attrData of initialData.attributes) { data.attributes.push(new Attribute(attrData)); } } if (data.type === 'skeleton') { data.sublabels = data.sublabels.map((internalLabel) => new Label({ ...internalLabel, has_parent: true })); } Object.defineProperties( this, Object.freeze({ id: { get: () => data.id, }, name: { get: () => data.name, set: (name) => { if (typeof name !== 'string') { throw new ArgumentError(`Name must be a string, but ${typeof name} was given`); } data.name = name; if (Number.isInteger(data.id)) { data.patched = true; } }, }, color: { get: () => data.color, set: (color) => { if (typeof color === 'string' && color.match(/^#[0-9a-f]{6}$|^$/)) { data.color = color; if (Number.isInteger(data.id)) { data.patched = true; } } else { throw new ArgumentError('Trying to set wrong color format'); } }, }, attributes: { get: () => [...data.attributes], }, type: { get: () => data.type, }, structure: { get: () => { if (data.type === ShapeType.SKELETON) { return { svg: data.svg, sublabels: [...data.sublabels], }; } return null; }, }, deleted: { get: () => data.deleted, set: (value) => { data.deleted = value; }, }, patched: { get: () => data.patched, set: (value) => { data.patched = value; }, }, hasParent: { get: () => data.has_parent, }, }), ); } toJSON(): SerializedLabel { const object: SerializedLabel = { name: this.name, attributes: [...this.attributes.map((el) => el.toJSON())], type: this.type, }; if (typeof this.color !== 'undefined') { object.color = this.color; } if (typeof this.id !== 'undefined') { object.id = this.id; } if (this.type) { object.type = this.type; } const { structure } = this; if (structure) { object.svg = structure.svg; object.sublabels = structure.sublabels.map((internalLabel) => internalLabel.toJSON()); } return object; } }