CVAT-3D Milestone2 (#2645)
* CVAT-3D Updated the Mime Types with Bin Support, added dependency of open3D * CVAT-3D Added additional column as Dimension for engine_task table and created a relatedfiles table for PCD to Image mapping. * Added Support for 3D file Upload in BIN and PCD. * Added Dimension attribute defaulting to 2D for importer and exporter. * Added props passing for dimension attribute, filtering of import, Migration Scripts and Dimension attribute for MpegChunk Writers * Modified code as per review comments * Updated Unit test cases for 3D task creation * Refactored Dimension Enum in UI and backend code * Resolving conflicts * Updated Unit Test Case * Refactored TaskDimension to DimensionType, Simplified usage of Dimension accross classes * Removing manually created test files * Removing old pcd mime-type mapping * Added test files generated by synthetic data using open3d * Merged with develop branch latest changes * Added libraries required for open3d * Added files * Added synthethic pcd,bin and img test files * Modified test file name * Trigger travis ci * Modified test case to ignore 3D preview images * Trigger notification * Deleting DS Store files * Modified test cases as per review comments * Checking pre-commit hook * Fixed Lint issues - precommit hook verification * Added changes for CVAT-3D Milestone2 changes - Frame Navigation, photo context hide and show * Modified changes * Added canvas3D for 3D Perspective * Added missing files * Added code to get image context for 3D view * Codacy check for stylesheet * Modified frame navigantion for 3D View * Modified style for context-image * Trigger notification * Added Support for 3D file Upload in BIN and PCD. * Added props passing for dimension attribute, filtering of import, Migration Scripts and Dimension attribute for MpegChunk Writers * Modified code as per review comments * Refactored Dimension Enum in UI and backend code * Merged with develop branch latest changes * Added files * Added changes for CVAT-3D Milestone2 changes - Frame Navigation, photo context hide and show * Modified changes * Added canvas3D for 3D Perspective * Added missing files * Added code to get image context for 3D view * Codacy check for stylesheet * Modified frame navigantion for 3D View * Modified style for context-image * Changed cvat-data lint issues * Modified to use opencv as per review comments * Removed unwanted imports * Fixed css and added usage of hooks * Merged Develop branch code * Removed unused data structures * Removed unused data structures * Refactored unused data structures * Added three js dependency in cvat ui package-lock.json * Merged develop branch code and refactored code * Fixed snyk issue * Modified Camera Icon in photo-context * Update icons.tsx * Remove unused svg file * Modified changelog file Co-authored-by: cdp <cdp123>main
parent
71e2ddbb59
commit
5c67846c20
@ -0,0 +1 @@
|
|||||||
|
webpack.config.js
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
node: true,
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
ecmaVersion: 6,
|
||||||
|
},
|
||||||
|
plugins: ['@typescript-eslint', 'import'],
|
||||||
|
extends: [
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'airbnb-typescript/base',
|
||||||
|
'plugin:import/errors',
|
||||||
|
'plugin:import/warnings',
|
||||||
|
'plugin:import/typescript',
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-explicit-any': 0,
|
||||||
|
'@typescript-eslint/indent': ['warn', 4],
|
||||||
|
'no-plusplus': 0,
|
||||||
|
'no-restricted-syntax': [
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
selector: 'ForOfStatement',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'max-len': ['error', { code: 120 }],
|
||||||
|
'no-continue': 0,
|
||||||
|
'func-names': 0,
|
||||||
|
'no-console': 0, // this rule deprecates console.log, console.warn etc. because 'it is not good in production code'
|
||||||
|
'lines-between-class-members': 0,
|
||||||
|
'import/prefer-default-export': 0, // works incorrect with interfaces
|
||||||
|
'newline-per-chained-call': 0, // makes code uglier
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
'import/resolver': {
|
||||||
|
node: {
|
||||||
|
extensions: ['.ts', '.js', '.json'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
dist
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
# Module CVAT-CANVAS-3D
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The CVAT module written in TypeScript language.
|
||||||
|
It presents a canvas to viewing, drawing and editing of 3D annotations.
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
If you make changes in this package, please do following:
|
||||||
|
|
||||||
|
- After not important changes (typos, backward compatible bug fixes, refactoring) do: `npm version patch`
|
||||||
|
- After changing API (backward compatible new features) do: `npm version minor`
|
||||||
|
- After changing API (changes that break backward compatibility) do: `npm version major`
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
- Building of the module from sources in the `dist` directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
npm run build -- --mode=development # without a minification
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Methods
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface Canvas3d {
|
||||||
|
html(): HTMLDivElement;
|
||||||
|
setup(frameData: any): void;
|
||||||
|
fitCanvas(): void;
|
||||||
|
mode(): Mode;
|
||||||
|
isAbleToChangeFrame(): boolean;
|
||||||
|
render(): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### WEB
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Create an instance of a canvas
|
||||||
|
const canvas = new window.canvas.Canvas3d();
|
||||||
|
|
||||||
|
console.log('Version ', window.canvas.CanvasVersion);
|
||||||
|
console.log('Current mode is ', window.canvas.mode());
|
||||||
|
|
||||||
|
// Put canvas to a html container
|
||||||
|
htmlContainer.appendChild(canvas.html());
|
||||||
|
canvas.fitCanvas();
|
||||||
|
```
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "cvat-canvas3d",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Part of Computer Vision Annotation Tool which presents its canvas3D library",
|
||||||
|
"main": "src/canvas3d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc && webpack --config ./webpack.config.js",
|
||||||
|
"server": "nodemon --watch config --exec 'webpack-dev-server --config ./webpack.config.js --mode=development --open'"
|
||||||
|
},
|
||||||
|
"author": "Intel",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "^7.5.5",
|
||||||
|
"@babel/core": "^7.5.5",
|
||||||
|
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
||||||
|
"@babel/plugin-proposal-optional-chaining": "^7.11.0",
|
||||||
|
"@babel/preset-env": "^7.5.5",
|
||||||
|
"@babel/preset-typescript": "^7.3.3",
|
||||||
|
"@types/node": "^12.6.8",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^1.13.0",
|
||||||
|
"@typescript-eslint/parser": "^1.13.0",
|
||||||
|
"babel-loader": "^8.0.6",
|
||||||
|
"css-loader": "^3.4.2",
|
||||||
|
"dts-bundle-webpack": "^1.0.2",
|
||||||
|
"eslint": "^6.1.0",
|
||||||
|
"eslint-config-airbnb-typescript": "^4.0.1",
|
||||||
|
"eslint-config-typescript-recommended": "^1.4.17",
|
||||||
|
"eslint-plugin-import": "^2.18.2",
|
||||||
|
"node-sass": "^4.14.1",
|
||||||
|
"nodemon": "^1.19.4",
|
||||||
|
"postcss-loader": "^3.0.0",
|
||||||
|
"postcss-preset-env": "^6.7.0",
|
||||||
|
"sass-loader": "^8.0.2",
|
||||||
|
"style-loader": "^1.0.0",
|
||||||
|
"typescript": "^3.5.3",
|
||||||
|
"webpack": "^4.44.2",
|
||||||
|
"webpack-cli": "^3.3.6",
|
||||||
|
"webpack-dev-server": "^3.11.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"three": "^0.125.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
parser: false,
|
||||||
|
plugins: {
|
||||||
|
'postcss-preset-env': {
|
||||||
|
browsers: '> 2.5%', // https://github.com/browserslist/browserslist
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import pjson from '../../package.json';
|
||||||
|
import { Canvas3dController, Canvas3dControllerImpl } from './canvas3dController';
|
||||||
|
import { Canvas3dModel, Canvas3dModelImpl, Mode } from './canvas3dModel';
|
||||||
|
import { Canvas3dView, Canvas3dViewImpl } from './canvas3dView';
|
||||||
|
import { Master } from './master';
|
||||||
|
|
||||||
|
const Canvas3dVersion = pjson.version;
|
||||||
|
|
||||||
|
interface Canvas3d {
|
||||||
|
html(): any;
|
||||||
|
setup(frameData: any): void;
|
||||||
|
isAbleToChangeFrame(): boolean;
|
||||||
|
fitCanvas(): void;
|
||||||
|
mode(): Mode;
|
||||||
|
render(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Canvas3dImpl implements Canvas3d {
|
||||||
|
private model: Canvas3dModel & Master;
|
||||||
|
private controller: Canvas3dController;
|
||||||
|
private view: Canvas3dView;
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
this.model = new Canvas3dModelImpl();
|
||||||
|
this.controller = new Canvas3dControllerImpl(this.model);
|
||||||
|
this.view = new Canvas3dViewImpl(this.model, this.controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
public html(): any {
|
||||||
|
return this.view.html();
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): void {
|
||||||
|
this.view.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setup(frameData: any): void {
|
||||||
|
this.model.setup(frameData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public mode(): Mode {
|
||||||
|
return this.model.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isAbleToChangeFrame(): boolean {
|
||||||
|
return this.model.isAbleToChangeFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
public fitCanvas(): void {
|
||||||
|
this.model.fitCanvas(this.view.html().clientWidth, this.view.html().clientHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Canvas3dImpl as Canvas3d, Canvas3dVersion };
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import { Canvas3dModel, Mode } from './canvas3dModel';
|
||||||
|
|
||||||
|
export interface Canvas3dController {
|
||||||
|
mode: Mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Canvas3dControllerImpl implements Canvas3dController {
|
||||||
|
private model: Canvas3dModel;
|
||||||
|
|
||||||
|
public constructor(model: Canvas3dModel) {
|
||||||
|
this.model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set mode(value: Mode) {
|
||||||
|
this.model.mode = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get mode(): Mode {
|
||||||
|
return this.model.mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,153 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import { MasterImpl } from './master';
|
||||||
|
|
||||||
|
export interface Size {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Image {
|
||||||
|
renderWidth: number;
|
||||||
|
renderHeight: number;
|
||||||
|
imageData: ImageData | CanvasImageSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DrawData {
|
||||||
|
enabled: boolean;
|
||||||
|
initialState?: any;
|
||||||
|
redraw?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FrameZoom {
|
||||||
|
MIN = 0.1,
|
||||||
|
MAX = 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum UpdateReasons {
|
||||||
|
IMAGE_CHANGED = 'image_changed',
|
||||||
|
OBJECTS_UPDATED = 'objects_updated',
|
||||||
|
FITTED_CANVAS = 'fitted_canvas',
|
||||||
|
DRAW = 'draw',
|
||||||
|
SELECT = 'select',
|
||||||
|
CANCEL = 'cancel',
|
||||||
|
DATA_FAILED = 'data_failed',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Mode {
|
||||||
|
IDLE = 'idle',
|
||||||
|
DRAG = 'drag',
|
||||||
|
RESIZE = 'resize',
|
||||||
|
DRAW = 'draw',
|
||||||
|
EDIT = 'edit',
|
||||||
|
INTERACT = 'interact',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Canvas3dModel {
|
||||||
|
mode: Mode;
|
||||||
|
|
||||||
|
setup(frameData: any): void;
|
||||||
|
|
||||||
|
isAbleToChangeFrame(): boolean;
|
||||||
|
|
||||||
|
fitCanvas(width: number, height: number): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Canvas3dModelImpl extends MasterImpl implements Canvas3dModel {
|
||||||
|
private data: {
|
||||||
|
canvasSize: Size;
|
||||||
|
image: Image | null;
|
||||||
|
imageID: number | null;
|
||||||
|
imageOffset: number;
|
||||||
|
imageSize: Size;
|
||||||
|
drawData: DrawData;
|
||||||
|
mode: Mode;
|
||||||
|
exception: Error | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
super();
|
||||||
|
this.data = {
|
||||||
|
canvasSize: {
|
||||||
|
height: 0,
|
||||||
|
width: 0,
|
||||||
|
},
|
||||||
|
image: null,
|
||||||
|
imageID: null,
|
||||||
|
imageOffset: 0,
|
||||||
|
imageSize: {
|
||||||
|
height: 0,
|
||||||
|
width: 0,
|
||||||
|
},
|
||||||
|
drawData: {
|
||||||
|
enabled: false,
|
||||||
|
initialState: null,
|
||||||
|
},
|
||||||
|
mode: Mode.IDLE,
|
||||||
|
exception: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public setup(frameData: any): void {
|
||||||
|
if (this.data.imageID !== frameData.number) {
|
||||||
|
if ([Mode.EDIT, Mode.DRAG, Mode.RESIZE].includes(this.data.mode)) {
|
||||||
|
throw Error(`Canvas is busy. Action: ${this.data.mode}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data.imageID = frameData.number;
|
||||||
|
frameData
|
||||||
|
.data((): void => {
|
||||||
|
this.data.image = null;
|
||||||
|
this.notify(UpdateReasons.IMAGE_CHANGED);
|
||||||
|
})
|
||||||
|
.then((data: Image): void => {
|
||||||
|
if (frameData.number !== this.data.imageID) {
|
||||||
|
// already another image
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data.imageSize = {
|
||||||
|
height: frameData.height as number,
|
||||||
|
width: frameData.width as number,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.data.image = data;
|
||||||
|
this.notify(UpdateReasons.IMAGE_CHANGED);
|
||||||
|
})
|
||||||
|
.catch((exception: any): void => {
|
||||||
|
this.data.exception = exception;
|
||||||
|
this.notify(UpdateReasons.DATA_FAILED);
|
||||||
|
throw exception;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public set mode(value: Mode) {
|
||||||
|
this.data.mode = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get mode(): Mode {
|
||||||
|
return this.data.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isAbleToChangeFrame(): boolean {
|
||||||
|
const isUnable = [Mode.DRAG, Mode.EDIT, Mode.RESIZE, Mode.INTERACT].includes(this.data.mode)
|
||||||
|
|| (this.data.mode === Mode.DRAW && typeof this.data.drawData.redraw === 'number');
|
||||||
|
|
||||||
|
return !isUnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public fitCanvas(width: number, height: number): void {
|
||||||
|
this.data.canvasSize.height = height;
|
||||||
|
this.data.canvasSize.width = width;
|
||||||
|
|
||||||
|
this.data.imageOffset = Math.floor(
|
||||||
|
Math.max(this.data.canvasSize.height / FrameZoom.MIN, this.data.canvasSize.width / FrameZoom.MIN),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.notify(UpdateReasons.FITTED_CANVAS);
|
||||||
|
this.notify(UpdateReasons.OBJECTS_UPDATED);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import * as THREE from 'three';
|
||||||
|
import { PCDLoader } from 'three/examples/jsm/loaders/PCDLoader';
|
||||||
|
import { Canvas3dController } from './canvas3dController';
|
||||||
|
import { Listener, Master } from './master';
|
||||||
|
|
||||||
|
import { Canvas3dModel, UpdateReasons, Mode } from './canvas3dModel';
|
||||||
|
|
||||||
|
export interface Canvas3dView {
|
||||||
|
html(): HTMLDivElement;
|
||||||
|
render(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Canvas3dViewImpl implements Canvas3dView, Listener {
|
||||||
|
private controller: Canvas3dController;
|
||||||
|
private renderer: any;
|
||||||
|
private scene: any;
|
||||||
|
private camera: any;
|
||||||
|
|
||||||
|
private set mode(value: Mode) {
|
||||||
|
this.controller.mode = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get mode(): Mode {
|
||||||
|
return this.controller.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(model: Canvas3dModel & Master, controller: Canvas3dController) {
|
||||||
|
this.controller = controller;
|
||||||
|
|
||||||
|
this.mode = Mode.IDLE;
|
||||||
|
|
||||||
|
this.scene = new THREE.Scene();
|
||||||
|
this.scene.background = new THREE.Color(0x000000);
|
||||||
|
|
||||||
|
// setting up the camera and adding it in the scene
|
||||||
|
this.camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 500);
|
||||||
|
this.camera.position.set(-15, 0, 4);
|
||||||
|
this.camera.up.set(0, 0, 1);
|
||||||
|
this.camera.lookAt(0, 0, 0);
|
||||||
|
this.scene.add(this.camera);
|
||||||
|
|
||||||
|
this.renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||||
|
this.renderer.setPixelRatio(window.devicePixelRatio);
|
||||||
|
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
|
||||||
|
model.subscribe(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public notify(model: Canvas3dModel & Master, reason: UpdateReasons): void {
|
||||||
|
if (reason === UpdateReasons.IMAGE_CHANGED) {
|
||||||
|
const loader = new PCDLoader();
|
||||||
|
this.clearScene();
|
||||||
|
const objectURL = URL.createObjectURL(model.data.image.imageData);
|
||||||
|
loader.load(objectURL, this.addScene.bind(this));
|
||||||
|
URL.revokeObjectURL(objectURL);
|
||||||
|
const event: CustomEvent = new CustomEvent('canvas.setup');
|
||||||
|
this.renderer.domElement.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private clearScene(): void {
|
||||||
|
for (let i = this.scene.children.length - 1; i >= 0; i--) {
|
||||||
|
this.scene.remove(this.scene.children[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private addScene(points: any): void {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
points.material.size = 0.03;
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
points.material.color = new THREE.Color(0x0000ff);
|
||||||
|
this.scene.add(points);
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): void {
|
||||||
|
this.renderer.render(this.scene, this.camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
public html(): any {
|
||||||
|
return this.renderer.domElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
const BASE_GRID_WIDTH = 2;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
BASE_GRID_WIDTH,
|
||||||
|
};
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
export interface Master {
|
||||||
|
subscribe(listener: Listener): void;
|
||||||
|
unsubscribe(listener: Listener): void;
|
||||||
|
unsubscribeAll(): void;
|
||||||
|
notify(reason: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Listener {
|
||||||
|
notify(master: Master, reason: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MasterImpl implements Master {
|
||||||
|
private listeners: Listener[];
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
this.listeners = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public subscribe(listener: Listener): void {
|
||||||
|
this.listeners.push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsubscribe(listener: Listener): void {
|
||||||
|
for (let i = 0; i < this.listeners.length; i++) {
|
||||||
|
if (this.listeners[i] === listener) {
|
||||||
|
this.listeners.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsubscribeAll(): void {
|
||||||
|
this.listeners = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public notify(reason: string): void {
|
||||||
|
for (const listener of this.listeners) {
|
||||||
|
listener.notify(this, reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
|
"module": "es6",
|
||||||
|
"target": "es6",
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"preserveConstEnums": true,
|
||||||
|
"declaration": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"declarationDir": "dist/declaration",
|
||||||
|
"paths": {
|
||||||
|
"cvat-canvas.node": ["dist/cvat-canvas3d.node"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/typescript/*.ts"]
|
||||||
|
}
|
||||||
@ -0,0 +1,138 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const DtsBundleWebpack = require('dts-bundle-webpack');
|
||||||
|
|
||||||
|
const nodeConfig = {
|
||||||
|
target: 'node',
|
||||||
|
mode: 'production',
|
||||||
|
devtool: 'source-map',
|
||||||
|
entry: './src/typescript/canvas3d.ts',
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
filename: 'cvat-canvas3d.node.js',
|
||||||
|
library: 'canvas3d',
|
||||||
|
libraryTarget: 'commonjs',
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.ts', '.js', '.json'],
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.ts$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: {
|
||||||
|
loader: 'babel-loader',
|
||||||
|
options: {
|
||||||
|
plugins: [
|
||||||
|
'@babel/plugin-proposal-class-properties',
|
||||||
|
'@babel/plugin-proposal-optional-chaining',
|
||||||
|
],
|
||||||
|
presets: [['@babel/preset-env'], ['@babel/typescript']],
|
||||||
|
sourceType: 'unambiguous',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(css|scss)$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: [
|
||||||
|
'style-loader',
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
importLoaders: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'postcss-loader',
|
||||||
|
'sass-loader',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new DtsBundleWebpack({
|
||||||
|
name: 'cvat-canvas3d.node',
|
||||||
|
main: 'dist/declaration/src/typescript/canvas3d.d.ts',
|
||||||
|
out: '../cvat-canvas3d.node.d.ts',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const webConfig = {
|
||||||
|
target: 'web',
|
||||||
|
mode: 'production',
|
||||||
|
devtool: 'source-map',
|
||||||
|
entry: {
|
||||||
|
'cvat-canvas3d': './src/typescript/canvas3d.ts',
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
filename: '[name].[contenthash].js',
|
||||||
|
library: 'canvas3d',
|
||||||
|
libraryTarget: 'window',
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
contentBase: path.join(__dirname, 'dist'),
|
||||||
|
compress: false,
|
||||||
|
inline: true,
|
||||||
|
port: 3000,
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.ts', '.js', '.json'],
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.ts$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: {
|
||||||
|
loader: 'babel-loader',
|
||||||
|
options: {
|
||||||
|
plugins: ['@babel/plugin-proposal-class-properties'],
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'@babel/preset-env',
|
||||||
|
{
|
||||||
|
targets: '> 2.5%', // https://github.com/browserslist/browserslist
|
||||||
|
},
|
||||||
|
],
|
||||||
|
['@babel/typescript'],
|
||||||
|
],
|
||||||
|
sourceType: 'unambiguous',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.scss$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: [
|
||||||
|
'style-loader',
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
importLoaders: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'postcss-loader',
|
||||||
|
'sass-loader',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new DtsBundleWebpack({
|
||||||
|
name: 'cvat-canvas3d',
|
||||||
|
main: 'dist/declaration/src/typescript/canvas3d.d.ts',
|
||||||
|
out: '../cvat-canvas3d.d.ts',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = [webConfig, nodeConfig];
|
||||||
@ -0,0 +1,109 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import React, { ReactElement, useEffect, useRef } from 'react';
|
||||||
|
import { GlobalHotKeys } from 'react-hotkeys';
|
||||||
|
import Layout from 'antd/lib/layout/layout';
|
||||||
|
|
||||||
|
import { Workspace } from 'reducers/interfaces';
|
||||||
|
import { Canvas3d } from 'cvat-canvas3d-wrapper';
|
||||||
|
import ContextImage from '../standard3D-workspace/context-image/context-image';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas3d;
|
||||||
|
jobInstance: any;
|
||||||
|
frameData: any;
|
||||||
|
curZLayer: number;
|
||||||
|
contextImageHide: boolean;
|
||||||
|
loaded: boolean;
|
||||||
|
data: string;
|
||||||
|
annotations: any[];
|
||||||
|
onSetupCanvas: () => void;
|
||||||
|
getContextImage(): void;
|
||||||
|
workspace: Workspace;
|
||||||
|
animateID: any;
|
||||||
|
automaticBordering: boolean;
|
||||||
|
showObjectsTextAlways: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CanvasWrapperComponent = (props: Props): ReactElement => {
|
||||||
|
const animateId = useRef(0);
|
||||||
|
const cvatCanvasContainerRef = useRef();
|
||||||
|
|
||||||
|
const {
|
||||||
|
frameData, contextImageHide, getContextImage, loaded, data, annotations, curZLayer,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const fitCanvas = (): void => {
|
||||||
|
const { canvasInstance } = props;
|
||||||
|
canvasInstance.fitCanvas();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCanvasSetup = (): void => {
|
||||||
|
const { onSetupCanvas } = props;
|
||||||
|
onSetupCanvas();
|
||||||
|
};
|
||||||
|
|
||||||
|
const animateCanvas = (): void => {
|
||||||
|
const { canvasInstance } = props;
|
||||||
|
|
||||||
|
canvasInstance.render();
|
||||||
|
animateId.current = requestAnimationFrame(animateCanvas);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateCanvas = (): void => {
|
||||||
|
const { canvasInstance } = props;
|
||||||
|
|
||||||
|
if (frameData !== null) {
|
||||||
|
canvasInstance.setup(frameData);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialSetup = (): void => {
|
||||||
|
const { canvasInstance } = props;
|
||||||
|
|
||||||
|
// Size
|
||||||
|
window.addEventListener('resize', fitCanvas);
|
||||||
|
fitCanvas();
|
||||||
|
|
||||||
|
// Events
|
||||||
|
canvasInstance.html().addEventListener('canvas.setup', onCanvasSetup);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const { canvasInstance } = props;
|
||||||
|
|
||||||
|
cvatCanvasContainerRef.current.appendChild(canvasInstance.html());
|
||||||
|
|
||||||
|
initialSetup();
|
||||||
|
updateCanvas();
|
||||||
|
animateCanvas();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
canvasInstance.html().removeEventListener('canvas.setup', onCanvasSetup);
|
||||||
|
window.removeEventListener('resize', fitCanvas);
|
||||||
|
cancelAnimationFrame(animateId.current);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateCanvas();
|
||||||
|
}, [frameData, annotations, curZLayer]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout.Content style={{ position: 'relative' }}>
|
||||||
|
<GlobalHotKeys />
|
||||||
|
<ContextImage
|
||||||
|
frame={frameData}
|
||||||
|
contextImageHide={contextImageHide}
|
||||||
|
getContextImage={getContextImage}
|
||||||
|
loaded={loaded}
|
||||||
|
data={data}
|
||||||
|
/>
|
||||||
|
<div ref={cvatCanvasContainerRef} className='cvat-canvas-container cvat-canvas-container-overflow' />
|
||||||
|
</Layout.Content>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(CanvasWrapperComponent);
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
frame: number;
|
||||||
|
contextImageHide: boolean;
|
||||||
|
loaded: boolean;
|
||||||
|
data: string;
|
||||||
|
getContextImage(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ContextImage(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
contextImageHide, loaded, data, getContextImage,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!contextImageHide && !loaded) {
|
||||||
|
getContextImage();
|
||||||
|
}
|
||||||
|
}, [contextImageHide, loaded]);
|
||||||
|
|
||||||
|
if (!contextImageHide && data !== '') {
|
||||||
|
return (
|
||||||
|
<div className='cvat-contextImage'>
|
||||||
|
<img src={data} alt='Context not available' className='cvat-contextImage-show' />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { GlobalHotKeys } from 'react-hotkeys';
|
||||||
|
import Layout from 'antd/lib/layout';
|
||||||
|
|
||||||
|
import { ActiveControl } from 'reducers/interfaces';
|
||||||
|
import { Canvas3d as Canvas } from 'cvat-canvas3d-wrapper';
|
||||||
|
|
||||||
|
import CursorControl from './cursor-control';
|
||||||
|
import MoveControl from './move-control';
|
||||||
|
|
||||||
|
import DrawCuboidControl from './draw-cuboid-control';
|
||||||
|
|
||||||
|
import PhotoContextControl from './photo-context';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
normalizedKeyMap: Record<string, string>;
|
||||||
|
contextImageHide: boolean;
|
||||||
|
hideShowContextImage: (hidden: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ControlsSideBarComponent(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
canvasInstance, activeControl, normalizedKeyMap, contextImageHide, hideShowContextImage,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout.Sider className='cvat-canvas-controls-sidebar' theme='light' width={44}>
|
||||||
|
<GlobalHotKeys />
|
||||||
|
|
||||||
|
<MoveControl canvasInstance={canvasInstance} activeControl={activeControl} />
|
||||||
|
|
||||||
|
<CursorControl
|
||||||
|
cursorShortkey={normalizedKeyMap.CANCEL}
|
||||||
|
canvasInstance={canvasInstance}
|
||||||
|
activeControl={activeControl}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DrawCuboidControl
|
||||||
|
canvasInstance={canvasInstance}
|
||||||
|
isDrawing={activeControl === ActiveControl.DRAW_CUBOID}
|
||||||
|
/>
|
||||||
|
<PhotoContextControl
|
||||||
|
canvasInstance={canvasInstance}
|
||||||
|
activeControl={activeControl}
|
||||||
|
contextImageHide={contextImageHide}
|
||||||
|
hideShowContextImage={hideShowContextImage}
|
||||||
|
/>
|
||||||
|
</Layout.Sider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import Icon from '@ant-design/icons';
|
||||||
|
import Tooltip from 'antd/lib/tooltip';
|
||||||
|
|
||||||
|
import { CursorIcon } from 'icons';
|
||||||
|
import { ActiveControl } from 'reducers/interfaces';
|
||||||
|
import { Canvas3d as Canvas } from 'cvat-canvas3d-wrapper';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
cursorShortkey: string;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CursorControl(props: Props): JSX.Element {
|
||||||
|
const { activeControl, cursorShortkey } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip title={`Cursor ${cursorShortkey}`} placement='right' mouseLeaveDelay={0}>
|
||||||
|
<Icon
|
||||||
|
component={CursorIcon}
|
||||||
|
className={[
|
||||||
|
'cvat-cursor-control',
|
||||||
|
activeControl === ActiveControl.CURSOR ? 'cvat-active-canvas-control ' : null,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(CursorControl);
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import Popover from 'antd/lib/popover';
|
||||||
|
import Icon from '@ant-design/icons';
|
||||||
|
|
||||||
|
import { Canvas3d as Canvas } from 'cvat-canvas3d-wrapper';
|
||||||
|
import { ShapeType } from 'reducers/interfaces';
|
||||||
|
|
||||||
|
import { CubeIcon } from 'icons';
|
||||||
|
|
||||||
|
import DrawShapePopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
isDrawing: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DrawPolygonControl(props: Props): JSX.Element {
|
||||||
|
const { canvasInstance, isDrawing } = props;
|
||||||
|
|
||||||
|
const dynamcPopoverPros = isDrawing ?
|
||||||
|
{
|
||||||
|
overlayStyle: {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
} :
|
||||||
|
{};
|
||||||
|
|
||||||
|
const dynamicIconProps = isDrawing ?
|
||||||
|
{
|
||||||
|
className: 'cvat-draw-cuboid-control cvat-active-canvas-control',
|
||||||
|
onClick: (): void => {
|
||||||
|
canvasInstance.draw({ enabled: false });
|
||||||
|
},
|
||||||
|
} :
|
||||||
|
{
|
||||||
|
className: 'cvat-draw-cuboid-control',
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
{...dynamcPopoverPros}
|
||||||
|
overlayClassName='cvat-draw-shape-popover'
|
||||||
|
placement='right'
|
||||||
|
content={<DrawShapePopoverContainer shapeType={ShapeType.CUBOID} />}
|
||||||
|
>
|
||||||
|
<Icon {...dynamicIconProps} component={CubeIcon} />
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(DrawPolygonControl);
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import Icon from '@ant-design/icons';
|
||||||
|
import Tooltip from 'antd/lib/tooltip';
|
||||||
|
|
||||||
|
import { MoveIcon } from 'icons';
|
||||||
|
import { ActiveControl } from 'reducers/interfaces';
|
||||||
|
import { Canvas3d as Canvas } from 'cvat-canvas3d-wrapper';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
function MoveControl(props: Props): JSX.Element {
|
||||||
|
const { activeControl } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip title='Move the image' placement='right' mouseLeaveDelay={0}>
|
||||||
|
<Icon
|
||||||
|
component={MoveIcon}
|
||||||
|
className={[
|
||||||
|
'cvat-move-control',
|
||||||
|
activeControl === ActiveControl.DRAG_CANVAS ? ' cvat-active-canvas-control' : null,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(MoveControl);
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import CameraIcon from '@ant-design/icons/CameraOutlined';
|
||||||
|
import Tooltip from 'antd/lib/tooltip';
|
||||||
|
import { Canvas3d as Canvas } from 'cvat-canvas3d-wrapper';
|
||||||
|
import React from 'react';
|
||||||
|
import { ActiveControl } from 'reducers/interfaces';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
hideShowContextImage: (hidden: boolean) => void;
|
||||||
|
contextImageHide: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function PhotoContextControl(props: Props): JSX.Element {
|
||||||
|
const { activeControl, contextImageHide, hideShowContextImage } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip title='Photo context show/hide' placement='right' mouseLeaveDelay={0}>
|
||||||
|
<CameraIcon
|
||||||
|
className={`cvat-move-control
|
||||||
|
cvat-control-side-bar-icon-size ${
|
||||||
|
activeControl === ActiveControl.PHOTO_CONTEXT ? 'cvat-active-canvas-control' : ''
|
||||||
|
}`}
|
||||||
|
onClick={(): void => {
|
||||||
|
hideShowContextImage(!contextImageHide);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(PhotoContextControl);
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import './styles.scss';
|
||||||
|
import React from 'react';
|
||||||
|
import Layout from 'antd/lib/layout';
|
||||||
|
|
||||||
|
import CanvasWrapperContainer from 'containers/annotation-page/canvas/canvas-wrapper3D';
|
||||||
|
import ControlsSideBarContainer from 'containers/annotation-page/standard3D-workspace/controls-side-bar/controls-side-bar';
|
||||||
|
|
||||||
|
export default function StandardWorkspace3DComponent(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Layout hasSider className='cvat-standard-workspace'>
|
||||||
|
<ControlsSideBarContainer />
|
||||||
|
<CanvasWrapperContainer />
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,173 @@
|
|||||||
|
// Copyright (C) 2020 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
@import 'base.scss';
|
||||||
|
|
||||||
|
.cvat-standard-workspace.ant-layout {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-contextImage {
|
||||||
|
width: $grid-unit-size * 32;
|
||||||
|
position: absolute;
|
||||||
|
background: $border-color-3;
|
||||||
|
top: $grid-unit-size;
|
||||||
|
right: $grid-unit-size;
|
||||||
|
max-height: $grid-unit-size * 16;
|
||||||
|
z-index: 100;
|
||||||
|
border-radius: $grid-unit-size;
|
||||||
|
border: 1px solid $border-color-3;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: $grid-unit-size/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-contextImage-show {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-contextImage-loading {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-objects-sidebar-filter-input {
|
||||||
|
width: calc(100% - 35px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-objects-sidebar-sider {
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
left: auto;
|
||||||
|
background-color: $background-color-2;
|
||||||
|
border-left: 1px solid $border-color-1;
|
||||||
|
border-bottom: 1px solid $border-color-1;
|
||||||
|
border-radius: $grid-unit-size/2 0 0 $grid-unit-size/2;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-objects-sidebar {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-rotate-canvas-controls-right > svg {
|
||||||
|
transform: scaleX(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-canvas-controls-sidebar {
|
||||||
|
background-color: $background-color-2;
|
||||||
|
border-right: 1px solid $border-color-1;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
> i {
|
||||||
|
border-radius: 3.3px;
|
||||||
|
transform: scale(0.65);
|
||||||
|
padding: $grid-unit-size/4;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $header-color;
|
||||||
|
transform: scale(0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.65);
|
||||||
|
}
|
||||||
|
|
||||||
|
> svg {
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-active-canvas-control {
|
||||||
|
background: $header-color;
|
||||||
|
transform: scale(0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-rotate-canvas-controls-left,
|
||||||
|
.cvat-rotate-canvas-controls-right {
|
||||||
|
transform: scale(0.65);
|
||||||
|
border-radius: $grid-unit-size/2;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.65);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-rotate-canvas-controls > .ant-popover-content > .ant-popover-inner > div > .ant-popover-inner-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-draw-shape-popover,
|
||||||
|
.cvat-tools-control-popover {
|
||||||
|
> .ant-popover-content > .ant-popover-inner > div > .ant-popover-inner-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-tools-track-button,
|
||||||
|
.cvat-tools-interact-button {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: $grid-unit-size;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-draw-shape-popover-points-selector {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-tools-control-popover-content {
|
||||||
|
width: fit-content;
|
||||||
|
padding: $grid-unit-size;
|
||||||
|
border-radius: $grid-unit-size/2;
|
||||||
|
background: $background-color-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-draw-shape-popover-content {
|
||||||
|
padding: $grid-unit-size;
|
||||||
|
border-radius: $grid-unit-size/2;
|
||||||
|
background: $background-color-2;
|
||||||
|
width: 270px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
margin-top: $grid-unit-size/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div:nth-child(3) > div > div {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div:last-child {
|
||||||
|
span {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&:nth-child(1) {
|
||||||
|
border-radius: $grid-unit-size/2 0 0 $grid-unit-size/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(2) {
|
||||||
|
border-radius: 0 $grid-unit-size/2 $grid-unit-size/2 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-canvas-container-overflow {
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-control-side-bar-icon-size {
|
||||||
|
font-size: $grid-unit-size * 5;
|
||||||
|
}
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import CanvasWrapperComponent from 'components/annotation-page/canvas/canvas-wrapper3D';
|
||||||
|
import { confirmCanvasReady, getContextImage } from 'actions/annotation-actions';
|
||||||
|
|
||||||
|
import { CombinedState } from 'reducers/interfaces';
|
||||||
|
|
||||||
|
import { Canvas3d } from 'cvat-canvas3d-wrapper';
|
||||||
|
|
||||||
|
interface StateToProps {
|
||||||
|
canvasInstance: Canvas3d;
|
||||||
|
jobInstance: any;
|
||||||
|
frameData: any;
|
||||||
|
curZLayer: number;
|
||||||
|
contextImageHide: boolean;
|
||||||
|
loaded: boolean;
|
||||||
|
data: string;
|
||||||
|
annotations: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DispatchToProps {
|
||||||
|
onSetupCanvas(): void;
|
||||||
|
getContextImage(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: CombinedState): StateToProps {
|
||||||
|
const {
|
||||||
|
annotation: {
|
||||||
|
canvas: { instance: canvasInstance },
|
||||||
|
job: { instance: jobInstance },
|
||||||
|
player: {
|
||||||
|
frame: { data: frameData },
|
||||||
|
contextImage: { hidden: contextImageHide, data, loaded },
|
||||||
|
},
|
||||||
|
annotations: {
|
||||||
|
states: annotations,
|
||||||
|
zLayer: { cur: curZLayer },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} = state;
|
||||||
|
|
||||||
|
return {
|
||||||
|
canvasInstance,
|
||||||
|
jobInstance,
|
||||||
|
frameData,
|
||||||
|
curZLayer,
|
||||||
|
contextImageHide,
|
||||||
|
loaded,
|
||||||
|
data,
|
||||||
|
annotations,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps(dispatch: any): DispatchToProps {
|
||||||
|
return {
|
||||||
|
onSetupCanvas(): void {
|
||||||
|
dispatch(confirmCanvasReady());
|
||||||
|
},
|
||||||
|
getContextImage(): void {
|
||||||
|
dispatch(getContextImage());
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(CanvasWrapperComponent);
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import { ExtendedKeyMapOptions } from 'react-hotkeys';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { Canvas } from 'cvat-canvas-wrapper';
|
||||||
|
import { hideShowContextImage } from 'actions/annotation-actions';
|
||||||
|
import ControlsSideBarComponent from 'components/annotation-page/standard3D-workspace/controls-side-bar/controls-side-bar';
|
||||||
|
import { ActiveControl, CombinedState } from 'reducers/interfaces';
|
||||||
|
|
||||||
|
interface StateToProps {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
keyMap: Record<string, ExtendedKeyMapOptions>;
|
||||||
|
normalizedKeyMap: Record<string, string>;
|
||||||
|
contextImageHide: boolean;
|
||||||
|
loaded: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DispatchToProps {
|
||||||
|
hideShowContextImage(hidden: boolean): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: CombinedState): StateToProps {
|
||||||
|
const {
|
||||||
|
annotation: {
|
||||||
|
canvas: { instance: canvasInstance, activeControl },
|
||||||
|
player: {
|
||||||
|
contextImage: { hidden: contextImageHide, loaded },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
shortcuts: { keyMap, normalizedKeyMap },
|
||||||
|
} = state;
|
||||||
|
|
||||||
|
return {
|
||||||
|
canvasInstance,
|
||||||
|
activeControl,
|
||||||
|
normalizedKeyMap,
|
||||||
|
keyMap,
|
||||||
|
contextImageHide,
|
||||||
|
loaded,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchToProps(dispatch: any): DispatchToProps {
|
||||||
|
return {
|
||||||
|
hideShowContextImage(hidden: boolean): void {
|
||||||
|
dispatch(hideShowContextImage(hidden));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, dispatchToProps)(ControlsSideBarComponent);
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
// Copyright (C) 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import { Canvas3d, Canvas3dVersion } from 'cvat-canvas3d/src/typescript/canvas3d';
|
||||||
|
|
||||||
|
export { Canvas3d, Canvas3dVersion };
|
||||||
Loading…
Reference in New Issue