From f24ff4db2fb6b1731d4f70bdf20a40d186165720 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 30 Nov 2022 10:16:46 -0800 Subject: [PATCH 1/8] Some code refactoring --- cvat-canvas3d/src/typescript/canvas3dView.ts | 158 ++++++++----------- 1 file changed, 66 insertions(+), 92 deletions(-) diff --git a/cvat-canvas3d/src/typescript/canvas3dView.ts b/cvat-canvas3d/src/typescript/canvas3dView.ts index e875e420d0b6..3064218523ee 100644 --- a/cvat-canvas3d/src/typescript/canvas3dView.ts +++ b/cvat-canvas3d/src/typescript/canvas3dView.ts @@ -39,19 +39,17 @@ export interface RayCast { mouseVector: THREE.Vector2; } -export interface Views { - perspective: RenderView; - top: RenderView; - side: RenderView; - front: RenderView; -} +export type Views = { + [key in ViewType]: RenderView; +}; -export interface CubeObject { - perspective: THREE.Mesh; - top: THREE.Mesh; - side: THREE.Mesh; - front: THREE.Mesh; -} +export type CubeObject = { + [key in ViewType]: THREE.Mesh; +}; + +export type ViewsDOM = { + [key in ViewType]: HTMLCanvasElement; +}; export interface RenderView { renderer: THREE.WebGLRenderer; @@ -61,12 +59,34 @@ export interface RenderView { rayCaster?: RayCast; } -export interface ViewsDOM { - perspective: HTMLCanvasElement; - top: HTMLCanvasElement; - side: HTMLCanvasElement; - front: HTMLCanvasElement; -} +const initialCameraSettings: { + [key in ViewType]: { + position: [number, number, number], + lookAt: [number, number, number], + up: [number, number, number], + } +} = { + perspective: { + position: [-15, 0, 4], + lookAt: [0, 0, 0], + up: [0, 0, 1], + }, + top: { + position: [0, 0, 8], + lookAt: [0, 0, 0], + up: [0, 0, 1], + }, + side: { + position: [0, 8, 0], + lookAt: [0, 0, 0], + up: [0, 0, 1], + }, + front: { + position: [8, 0, 0], + lookAt: [0, 0, 0], + up: [0, 0, 1], + }, +}; export class Canvas3dViewImpl implements Canvas3dView, Listener { private controller: Canvas3dController; @@ -393,11 +413,6 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { // setting up the camera and adding it in the scene this.views.perspective.camera = new THREE.PerspectiveCamera(50, aspectRatio, 1, 500); - this.views.perspective.camera.position.set(-15, 0, 4); - this.views.perspective.camera.up.set(0, 0, 1); - this.views.perspective.camera.lookAt(10, 0, 0); - this.views.perspective.camera.name = 'cameraPerspective'; - this.views.top.camera = new THREE.OrthographicCamera( (-aspectRatio * viewSize) / 2 - 2, (aspectRatio * viewSize) / 2 + 2, @@ -406,12 +421,6 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { -50, 50, ); - - this.views.top.camera.position.set(0, 0, 5); - this.views.top.camera.lookAt(0, 0, 0); - this.views.top.camera.up.set(0, 0, 1); - this.views.top.camera.name = 'cameraTop'; - this.views.side.camera = new THREE.OrthographicCamera( (-aspectRatio * viewSize) / 2, (aspectRatio * viewSize) / 2, @@ -420,11 +429,6 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { -50, 50, ); - this.views.side.camera.position.set(0, 5, 0); - this.views.side.camera.lookAt(0, 0, 0); - this.views.side.camera.up.set(0, 0, 1); - this.views.side.camera.name = 'cameraSide'; - this.views.front.camera = new THREE.OrthographicCamera( (-aspectRatio * viewSize) / 2, (aspectRatio * viewSize) / 2, @@ -433,10 +437,18 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { -50, 50, ); - this.views.front.camera.position.set(3, 0, 0); - this.views.front.camera.up.set(0, 0, 1); - this.views.front.camera.lookAt(0, 0, 0); - this.views.front.camera.name = 'cameraFront'; + + for (const cameraType of [ + ViewType.PERSPECTIVE, + ViewType.TOP, + ViewType.SIDE, + ViewType.FRONT, + ]) { + this.views[cameraType].camera.position.set(...initialCameraSettings[cameraType].position); + this.views.perspective.camera.up.set(...initialCameraSettings[cameraType].up); + this.views.perspective.camera.lookAt(...initialCameraSettings[cameraType].lookAt); + this.views.perspective.camera.name = `camera${cameraType[0].toUpperCase()}${cameraType.slice(1)}`; + } Object.keys(this.views).forEach((view: string): void => { const viewType = this.views[view as keyof Views]; @@ -484,48 +496,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { } private setDefaultZoom(): void { - if (this.model.data.activeElement === 'null') { - Object.keys(this.views).forEach((view: string): void => { - const viewType = this.views[view as keyof Views]; - if (view !== ViewType.PERSPECTIVE) { - viewType.camera.zoom = CONST.FOV_DEFAULT; - viewType.camera.updateProjectionMatrix(); - } - }); - } else { - const canvasTop = this.views.top.renderer.domElement; - const bboxtop = new THREE.Box3().setFromObject(this.model.data.selected.top); - const x1 = Math.min( - canvasTop.offsetWidth / (bboxtop.max.x - bboxtop.min.x), - canvasTop.offsetHeight / (bboxtop.max.y - bboxtop.min.y), - ) * 0.4; - this.views.top.camera.zoom = x1 / 100; - this.views.top.camera.updateProjectionMatrix(); - this.views.top.camera.updateMatrix(); - this.setHelperSize(ViewType.TOP); - - const canvasFront = this.views.top.renderer.domElement; - const bboxfront = new THREE.Box3().setFromObject(this.model.data.selected.front); - const x2 = Math.min( - canvasFront.offsetWidth / (bboxfront.max.y - bboxfront.min.y), - canvasFront.offsetHeight / (bboxfront.max.z - bboxfront.min.z), - ) * 0.4; - this.views.front.camera.zoom = x2 / 100; - this.views.front.camera.updateProjectionMatrix(); - this.views.front.camera.updateMatrix(); - this.setHelperSize(ViewType.FRONT); - - const canvasSide = this.views.side.renderer.domElement; - const bboxside = new THREE.Box3().setFromObject(this.model.data.selected.side); - const x3 = Math.min( - canvasSide.offsetWidth / (bboxside.max.x - bboxside.min.x), - canvasSide.offsetHeight / (bboxside.max.z - bboxside.min.z), - ) * 0.4; - this.views.side.camera.zoom = x3 / 100; - this.views.side.camera.updateProjectionMatrix(); - this.views.side.camera.updateMatrix(); - this.setHelperSize(ViewType.SIDE); - } + } private startAction(view: any, event: MouseEvent): void { @@ -992,8 +963,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { points.material.size = 0.05; points.material.color.set(new THREE.Color(0xffffff)); const material = points.material.clone(); - const sphereCenter = points.geometry.boundingSphere.center; - const { radius } = points.geometry.boundingSphere; + const { radius, center: sphereCenter } = points.geometry.boundingSphere; if (!this.views.perspective.camera) return; const xRange = -radius / 2 < this.views.perspective.camera.position.x - sphereCenter.x && radius / 2 > this.views.perspective.camera.position.x - sphereCenter.x; @@ -1121,16 +1091,20 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { this.views.side.controls && this.views.front.controls ) { - this.views.perspective.controls.setLookAt(x - 8, y - 8, z + 3, x, y, z, animation); - this.views.top.camera.position.set(x, y, z + 8); - this.views.top.camera.lookAt(x, y, z); - this.views.top.camera.zoom = CONST.FOV_DEFAULT; - this.views.side.camera.position.set(x, y + 8, z); - this.views.side.camera.lookAt(x, y, z); - this.views.side.camera.zoom = CONST.FOV_DEFAULT; - this.views.front.camera.position.set(x + 8, y, z); - this.views.front.camera.lookAt(x, y, z); - this.views.front.camera.zoom = CONST.FOV_DEFAULT; + this.views.perspective.controls.setLookAt(x - 15, y, z + 4, x, y, z, animation); + for (const cameraType of [ + ViewType.TOP, + ViewType.SIDE, + ViewType.FRONT, + ]) { + this.views[cameraType].camera.position.set( + x + initialCameraSettings[cameraType].position[0], + y + initialCameraSettings[cameraType].position[1], + z + initialCameraSettings[cameraType].position[2], + ); + this.views[cameraType].camera.lookAt(x, y, z); + this.views[cameraType].camera.zoom = CONST.FOV_DEFAULT; + } } } From 28fcf60ec4e23b811699dee999dbb94ef279b491 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 30 Nov 2022 10:23:37 -0800 Subject: [PATCH 2/8] Returned removed code --- cvat-canvas3d/src/typescript/canvas3dView.ts | 47 ++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/cvat-canvas3d/src/typescript/canvas3dView.ts b/cvat-canvas3d/src/typescript/canvas3dView.ts index 3064218523ee..bc5640cf1dc4 100644 --- a/cvat-canvas3d/src/typescript/canvas3dView.ts +++ b/cvat-canvas3d/src/typescript/canvas3dView.ts @@ -445,8 +445,8 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { ViewType.FRONT, ]) { this.views[cameraType].camera.position.set(...initialCameraSettings[cameraType].position); - this.views.perspective.camera.up.set(...initialCameraSettings[cameraType].up); this.views.perspective.camera.lookAt(...initialCameraSettings[cameraType].lookAt); + this.views.perspective.camera.up.set(...initialCameraSettings[cameraType].up); this.views.perspective.camera.name = `camera${cameraType[0].toUpperCase()}${cameraType.slice(1)}`; } @@ -496,7 +496,48 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { } private setDefaultZoom(): void { - + if (this.model.data.activeElement === 'null') { + Object.keys(this.views).forEach((view: string): void => { + const viewType = this.views[view as keyof Views]; + if (view !== ViewType.PERSPECTIVE) { + viewType.camera.zoom = CONST.FOV_DEFAULT; + viewType.camera.updateProjectionMatrix(); + } + }); + } else { + const canvasTop = this.views.top.renderer.domElement; + const bboxtop = new THREE.Box3().setFromObject(this.model.data.selected.top); + const x1 = Math.min( + canvasTop.offsetWidth / (bboxtop.max.x - bboxtop.min.x), + canvasTop.offsetHeight / (bboxtop.max.y - bboxtop.min.y), + ) * 0.4; + this.views.top.camera.zoom = x1 / 100; + this.views.top.camera.updateProjectionMatrix(); + this.views.top.camera.updateMatrix(); + this.setHelperSize(ViewType.TOP); + + const canvasFront = this.views.top.renderer.domElement; + const bboxfront = new THREE.Box3().setFromObject(this.model.data.selected.front); + const x2 = Math.min( + canvasFront.offsetWidth / (bboxfront.max.y - bboxfront.min.y), + canvasFront.offsetHeight / (bboxfront.max.z - bboxfront.min.z), + ) * 0.4; + this.views.front.camera.zoom = x2 / 100; + this.views.front.camera.updateProjectionMatrix(); + this.views.front.camera.updateMatrix(); + this.setHelperSize(ViewType.FRONT); + + const canvasSide = this.views.side.renderer.domElement; + const bboxside = new THREE.Box3().setFromObject(this.model.data.selected.side); + const x3 = Math.min( + canvasSide.offsetWidth / (bboxside.max.x - bboxside.min.x), + canvasSide.offsetHeight / (bboxside.max.z - bboxside.min.z), + ) * 0.4; + this.views.side.camera.zoom = x3 / 100; + this.views.side.camera.updateProjectionMatrix(); + this.views.side.camera.updateMatrix(); + this.setHelperSize(ViewType.SIDE); + } } private startAction(view: any, event: MouseEvent): void { @@ -1091,7 +1132,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { this.views.side.controls && this.views.front.controls ) { - this.views.perspective.controls.setLookAt(x - 15, y, z + 4, x, y, z, animation); + this.views.perspective.controls.setLookAt(x - 8, y - 8, z + 3, x, y, z, animation); for (const cameraType of [ ViewType.TOP, ViewType.SIDE, From aef1e19fa41fcd862c006adecfaeb4b037663def Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 30 Nov 2022 10:30:35 -0800 Subject: [PATCH 3/8] Fixed front orientation --- cvat-canvas3d/src/typescript/canvas3dView.ts | 30 +++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/cvat-canvas3d/src/typescript/canvas3dView.ts b/cvat-canvas3d/src/typescript/canvas3dView.ts index bc5640cf1dc4..4355c33b30b9 100644 --- a/cvat-canvas3d/src/typescript/canvas3dView.ts +++ b/cvat-canvas3d/src/typescript/canvas3dView.ts @@ -68,7 +68,7 @@ const initialCameraSettings: { } = { perspective: { position: [-15, 0, 4], - lookAt: [0, 0, 0], + lookAt: [10, 0, 0], up: [0, 0, 1], }, top: { @@ -445,9 +445,9 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { ViewType.FRONT, ]) { this.views[cameraType].camera.position.set(...initialCameraSettings[cameraType].position); - this.views.perspective.camera.lookAt(...initialCameraSettings[cameraType].lookAt); - this.views.perspective.camera.up.set(...initialCameraSettings[cameraType].up); - this.views.perspective.camera.name = `camera${cameraType[0].toUpperCase()}${cameraType.slice(1)}`; + this.views[cameraType].camera.lookAt(...initialCameraSettings[cameraType].lookAt); + this.views[cameraType].camera.up.set(...initialCameraSettings[cameraType].up); + this.views[cameraType].camera.name = `camera${cameraType[0].toUpperCase()}${cameraType.slice(1)}`; } Object.keys(this.views).forEach((view: string): void => { @@ -1133,19 +1133,15 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { this.views.front.controls ) { this.views.perspective.controls.setLookAt(x - 8, y - 8, z + 3, x, y, z, animation); - for (const cameraType of [ - ViewType.TOP, - ViewType.SIDE, - ViewType.FRONT, - ]) { - this.views[cameraType].camera.position.set( - x + initialCameraSettings[cameraType].position[0], - y + initialCameraSettings[cameraType].position[1], - z + initialCameraSettings[cameraType].position[2], - ); - this.views[cameraType].camera.lookAt(x, y, z); - this.views[cameraType].camera.zoom = CONST.FOV_DEFAULT; - } + this.views.top.camera.position.set(x, y, z + 8); + this.views.top.camera.lookAt(x, y, z); + this.views.top.camera.zoom = CONST.FOV_DEFAULT; + this.views.side.camera.position.set(x, y + 8, z); + this.views.side.camera.lookAt(x, y, z); + this.views.side.camera.zoom = CONST.FOV_DEFAULT; + this.views.front.camera.position.set(x + 8, y, z); + this.views.front.camera.lookAt(x, y, z); + this.views.front.camera.zoom = CONST.FOV_DEFAULT; } } From 2f9706bdb5ed3b3d147305aa26d84c7b56e9c8c7 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 30 Nov 2022 10:40:05 -0800 Subject: [PATCH 4/8] Removed extra fields in the class --- cvat-canvas3d/src/typescript/canvas3dView.ts | 34 ++++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/cvat-canvas3d/src/typescript/canvas3dView.ts b/cvat-canvas3d/src/typescript/canvas3dView.ts index 4355c33b30b9..3fcc9b737840 100644 --- a/cvat-canvas3d/src/typescript/canvas3dView.ts +++ b/cvat-canvas3d/src/typescript/canvas3dView.ts @@ -94,8 +94,6 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { private clock: THREE.Clock; private speed: number; private cube: CuboidModel; - private highlighted: boolean; - private selected: CubeObject; private model: Canvas3dModel & Master; private action: any; private globalHelpers: any; @@ -113,8 +111,6 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { this.clock = new THREE.Clock(); this.speed = CONST.MOVEMENT_FACTOR; this.cube = new CuboidModel('line', '#ffffff'); - this.highlighted = false; - this.selected = this.cube; this.model = model; this.globalHelpers = { top: { @@ -1132,16 +1128,26 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { this.views.side.controls && this.views.front.controls ) { - this.views.perspective.controls.setLookAt(x - 8, y - 8, z + 3, x, y, z, animation); - this.views.top.camera.position.set(x, y, z + 8); - this.views.top.camera.lookAt(x, y, z); - this.views.top.camera.zoom = CONST.FOV_DEFAULT; - this.views.side.camera.position.set(x, y + 8, z); - this.views.side.camera.lookAt(x, y, z); - this.views.side.camera.zoom = CONST.FOV_DEFAULT; - this.views.front.camera.position.set(x + 8, y, z); - this.views.front.camera.lookAt(x, y, z); - this.views.front.camera.zoom = CONST.FOV_DEFAULT; + this.views.perspective.controls.setLookAt( + x + initialCameraSettings.perspective.position[0], + y - initialCameraSettings.perspective.position[1], + z + initialCameraSettings.perspective.position[2], + x, y, z, animation, + ); + + for (const cameraType of [ + ViewType.TOP, + ViewType.SIDE, + ViewType.FRONT, + ]) { + this.views[cameraType].camera.position.set( + x + initialCameraSettings[cameraType].position[0], + y + initialCameraSettings[cameraType].position[1], + z + initialCameraSettings[cameraType].position[2], + ); + this.views[cameraType].camera.lookAt(x, y, z); + this.views[cameraType].camera.zoom = CONST.FOV_DEFAULT; + } } } From b7e594e6c8e93ec35e1f1f5501ce8ac04e5019a2 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 30 Nov 2022 12:23:17 -0800 Subject: [PATCH 5/8] Enabled 'resetZoom' setting for 3D canvas --- cvat-canvas3d/src/typescript/canvas3d.ts | 6 + .../src/typescript/canvas3dController.ts | 8 +- cvat-canvas3d/src/typescript/canvas3dModel.ts | 24 +++ cvat-canvas3d/src/typescript/canvas3dView.ts | 137 ++++++++++-------- .../canvas/canvas-wrapper3D.tsx | 12 +- .../canvas/canvas-wrapper3D.tsx | 97 ++----------- 6 files changed, 132 insertions(+), 152 deletions(-) diff --git a/cvat-canvas3d/src/typescript/canvas3d.ts b/cvat-canvas3d/src/typescript/canvas3d.ts index faa79b03bcb0..0677e87d187a 100644 --- a/cvat-canvas3d/src/typescript/canvas3d.ts +++ b/cvat-canvas3d/src/typescript/canvas3d.ts @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -13,6 +14,7 @@ import { MouseInteraction, ShapeProperties, GroupData, + Configuration, } from './canvas3dModel'; import { Canvas3dView, Canvas3dViewImpl, ViewsDOM, CameraAction, @@ -94,6 +96,10 @@ class Canvas3dImpl implements Canvas3d { this.model.configureShapes(shapeProperties); } + public configure(configuration: Configuration): void { + this.model.configure(configuration); + } + public activate(clientID: number | null, attributeID: number | null = null): void { this.model.activate(String(clientID), attributeID); } diff --git a/cvat-canvas3d/src/typescript/canvas3dController.ts b/cvat-canvas3d/src/typescript/canvas3dController.ts index 4dc8818603c5..fadcfc606943 100644 --- a/cvat-canvas3d/src/typescript/canvas3dController.ts +++ b/cvat-canvas3d/src/typescript/canvas3dController.ts @@ -1,9 +1,10 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT import { - Canvas3dModel, Mode, DrawData, ActiveElement, FocusData, GroupData, + Canvas3dModel, Mode, DrawData, ActiveElement, FocusData, GroupData, Configuration, } from './canvas3dModel'; export interface Canvas3dController { @@ -12,6 +13,7 @@ export interface Canvas3dController { readonly selected: any; readonly focused: FocusData; readonly groupData: GroupData; + readonly configuration: Configuration; readonly imageIsDeleted: boolean; mode: Mode; group(groupData: GroupData): void; @@ -56,6 +58,10 @@ export class Canvas3dControllerImpl implements Canvas3dController { return this.model.groupData; } + public get configuration(): Configuration { + return this.model.configuration; + } + public group(groupData: GroupData): void { this.model.group(groupData); } diff --git a/cvat-canvas3d/src/typescript/canvas3dModel.ts b/cvat-canvas3d/src/typescript/canvas3dModel.ts index 85dcfb06dbd4..2065d7bc8aa8 100644 --- a/cvat-canvas3d/src/typescript/canvas3dModel.ts +++ b/cvat-canvas3d/src/typescript/canvas3dModel.ts @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -19,6 +20,10 @@ export interface GroupData { grouped?: []; } +export interface Configuration { + resetZoom?: boolean; +} + export interface Image { renderWidth: number; renderHeight: number; @@ -80,6 +85,7 @@ export enum UpdateReasons { SHAPE_ACTIVATED = 'shape_activated', GROUP = 'group', FITTED_CANVAS = 'fitted_canvas', + CONFIG_UPDATED = 'config_updated', } export enum Mode { @@ -112,6 +118,7 @@ export interface Canvas3dDataModel { selected: any; shapeProperties: ShapeProperties; groupData: GroupData; + configuration: Configuration; } export interface Canvas3dModel { @@ -119,6 +126,7 @@ export interface Canvas3dModel { data: Canvas3dDataModel; readonly imageIsDeleted: boolean; readonly groupData: GroupData; + readonly configuration: Configuration; setup(frameData: any, objectStates: any[]): void; isAbleToChangeFrame(): boolean; draw(drawData: DrawData): void; @@ -126,6 +134,7 @@ export interface Canvas3dModel { dragCanvas(enable: boolean): void; activate(clientID: string | null, attributeID: number | null): void; configureShapes(shapeProperties: any): void; + configure(configuration: Configuration): void; fit(): void; group(groupData: GroupData): void; destroy(): void; @@ -177,6 +186,9 @@ export class Canvas3dModelImpl extends MasterImpl implements Canvas3dModel { selectedOpacity: 60, colorBy: 'Label', }, + configuration: { + resetZoom: false, + }, }; } @@ -327,6 +339,14 @@ export class Canvas3dModelImpl extends MasterImpl implements Canvas3dModel { this.notify(UpdateReasons.GROUP); } + public configure(configuration: Configuration): void { + if (typeof configuration.resetZoom === 'boolean') { + this.data.configuration.resetZoom = configuration.resetZoom; + } + + this.notify(UpdateReasons.CONFIG_UPDATED); + } + public configureShapes(shapeProperties: ShapeProperties): void { this.data.drawData.enabled = false; this.data.mode = Mode.IDLE; @@ -341,6 +361,10 @@ export class Canvas3dModelImpl extends MasterImpl implements Canvas3dModel { this.notify(UpdateReasons.FITTED_CANVAS); } + public get configuration(): Configuration { + return { ...this.data.configuration }; + } + public get groupData(): GroupData { return { ...this.data.groupData }; } diff --git a/cvat-canvas3d/src/typescript/canvas3dView.ts b/cvat-canvas3d/src/typescript/canvas3dView.ts index 3fcc9b737840..0d2c3ed72969 100644 --- a/cvat-canvas3d/src/typescript/canvas3dView.ts +++ b/cvat-canvas3d/src/typescript/canvas3dView.ts @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -59,35 +60,6 @@ export interface RenderView { rayCaster?: RayCast; } -const initialCameraSettings: { - [key in ViewType]: { - position: [number, number, number], - lookAt: [number, number, number], - up: [number, number, number], - } -} = { - perspective: { - position: [-15, 0, 4], - lookAt: [10, 0, 0], - up: [0, 0, 1], - }, - top: { - position: [0, 0, 8], - lookAt: [0, 0, 0], - up: [0, 0, 1], - }, - side: { - position: [0, 8, 0], - lookAt: [0, 0, 0], - up: [0, 0, 1], - }, - front: { - position: [8, 0, 0], - lookAt: [0, 0, 0], - up: [0, 0, 1], - }, -}; - export class Canvas3dViewImpl implements Canvas3dView, Listener { private controller: Canvas3dController; private views: Views; @@ -97,6 +69,13 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { private model: Canvas3dModel & Master; private action: any; private globalHelpers: any; + private cameraSettings: { + [key in ViewType]: { + position: [number, number, number], + lookAt: [number, number, number], + up: [number, number, number], + } + }; private set mode(value: Mode) { this.controller.mode = value; @@ -126,6 +105,30 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { rotate: [], }, }; + + this.cameraSettings = { + perspective: { + position: [-15, 0, 8], + lookAt: [10, 0, 0], + up: [0, 0, 1], + }, + top: { + position: [0, 0, 8], + lookAt: [0, 0, 0], + up: [0, 0, 1], + }, + side: { + position: [0, 8, 0], + lookAt: [0, 0, 0], + up: [0, 0, 1], + }, + front: { + position: [8, 0, 0], + lookAt: [0, 0, 0], + up: [0, 0, 1], + }, + }; + this.action = { loading: false, oldState: '', @@ -440,9 +443,9 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { ViewType.SIDE, ViewType.FRONT, ]) { - this.views[cameraType].camera.position.set(...initialCameraSettings[cameraType].position); - this.views[cameraType].camera.lookAt(...initialCameraSettings[cameraType].lookAt); - this.views[cameraType].camera.up.set(...initialCameraSettings[cameraType].up); + this.views[cameraType].camera.position.set(...this.cameraSettings[cameraType].position); + this.views[cameraType].camera.lookAt(...this.cameraSettings[cameraType].lookAt); + this.views[cameraType].camera.up.set(...this.cameraSettings[cameraType].up); this.views[cameraType].camera.name = `camera${cameraType[0].toUpperCase()}${cameraType.slice(1)}`; } @@ -996,33 +999,44 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { } private addScene(points: any): void { + const getcameraSettingsToFitScene = ( + camera: THREE.PerspectiveCamera, + boundingBox: THREE.Box3, + ): [number, number, number] => { + const offset = 5; + const width = boundingBox.max.x - boundingBox.min.x; + const height = boundingBox.max.y - boundingBox.min.y; + + // find the maximum width or height, compute z to approximately fit the scene + const maxDim = Math.max(width, height); + const fov = camera.fov * (Math.PI / 180); + const cameraZ = Math.abs((maxDim / 8) * Math.tan(fov * 2)); + + return [ + boundingBox.min.x + offset, + boundingBox.max.y + offset, + cameraZ + offset, + ]; + }; + // eslint-disable-next-line no-param-reassign points.material.size = 0.05; points.material.color.set(new THREE.Color(0xffffff)); const material = points.material.clone(); - const { radius, center: sphereCenter } = points.geometry.boundingSphere; + // const { radius, center: sphereCenter } = points.geometry.boundingSphere; if (!this.views.perspective.camera) return; - const xRange = -radius / 2 < this.views.perspective.camera.position.x - sphereCenter.x && - radius / 2 > this.views.perspective.camera.position.x - sphereCenter.x; - const yRange = -radius / 2 < this.views.perspective.camera.position.y - sphereCenter.y && - radius / 2 > this.views.perspective.camera.position.y - sphereCenter.y; - const zRange = -radius / 2 < this.views.perspective.camera.position.z - sphereCenter.z && - radius / 2 > this.views.perspective.camera.position.z - sphereCenter.z; - let newX = 0; - let newY = 0; - let newZ = 0; - if (!xRange) { - newX = sphereCenter.x; - } - if (!yRange) { - newY = sphereCenter.y; - } - if (!zRange) { - newZ = sphereCenter.z; - } - if (newX || newY || newZ) { - this.action.frameCoordinates = { x: newX, y: newY, z: newZ }; - this.positionAllViews(newX, newY, newZ, false); + + if (this.model.configuration.resetZoom) { + points.geometry.computeBoundingBox(); + this.cameraSettings.perspective.position = getcameraSettingsToFitScene( + this.views.perspective.camera as THREE.PerspectiveCamera, points.geometry.boundingBox, + ); + this.positionAllViews( + this.action.frameCoordinates.x, + this.action.frameCoordinates.y, + this.action.frameCoordinates.z, + false, + ); } [ViewType.TOP, ViewType.SIDE, ViewType.FRONT].forEach((view: ViewType): void => { @@ -1129,9 +1143,9 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { this.views.front.controls ) { this.views.perspective.controls.setLookAt( - x + initialCameraSettings.perspective.position[0], - y - initialCameraSettings.perspective.position[1], - z + initialCameraSettings.perspective.position[2], + x + this.cameraSettings.perspective.position[0], + y - this.cameraSettings.perspective.position[1], + z + this.cameraSettings.perspective.position[2], x, y, z, animation, ); @@ -1141,9 +1155,9 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { ViewType.FRONT, ]) { this.views[cameraType].camera.position.set( - x + initialCameraSettings[cameraType].position[0], - y + initialCameraSettings[cameraType].position[1], - z + initialCameraSettings[cameraType].position[2], + x + this.cameraSettings[cameraType].position[0], + y + this.cameraSettings[cameraType].position[1], + z + this.cameraSettings[cameraType].position[2], ); this.views[cameraType].camera.lookAt(x, y, z); this.views[cameraType].camera.zoom = CONST.FOV_DEFAULT; @@ -1275,8 +1289,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { try { this.detachCamera(null); // eslint-disable-next-line no-empty - } catch (e) { - } finally { + } catch (e) { } finally { this.action.detachCam = false; } } diff --git a/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper3D.tsx b/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper3D.tsx index 1b8f57bb0f69..6add2289b7bb 100644 --- a/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper3D.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper3D.tsx @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -34,11 +35,9 @@ interface Props { canvasInstance: Canvas3d | Canvas; jobInstance: any; frameData: any; - curZLayer: number; annotations: any[]; contextMenuVisibility: boolean; activeLabelID: number; - activatedStateID: number | null; activeObjectType: ObjectType; onSetupCanvas: () => void; onGroupObjects: (enabled: boolean) => void; @@ -52,9 +51,8 @@ interface Props { onDragCanvas: (enabled: boolean) => void; onShapeDrawn: () => void; workspace: Workspace; - automaticBordering: boolean; - showObjectsTextAlways: boolean; frame: number; + resetZoom: boolean; } interface ViewSize { @@ -184,6 +182,7 @@ const CanvasWrapperComponent = (props: Props): ReactElement => { frame, jobInstance, activeLabelID, + resetZoom, activeObjectType, onShapeDrawn, onCreateAnnotations, @@ -258,6 +257,7 @@ const CanvasWrapperComponent = (props: Props): ReactElement => { canvasInstanceDOM.perspective.addEventListener('canvas.canceled', onCanvasCancel); canvasInstanceDOM.perspective.addEventListener('canvas.dragstart', onCanvasDragStart); canvasInstanceDOM.perspective.addEventListener('canvas.dragstop', onCanvasDragDone); + canvasInstance.configure({ resetZoom }); }; const keyControlsKeyDown = (key: KeyboardEvent): void => { @@ -336,6 +336,10 @@ const CanvasWrapperComponent = (props: Props): ReactElement => { }; }, []); + useEffect(() => { + canvasInstance.configure({ resetZoom }); + }, [resetZoom]); + const updateShapesView = (): void => { (canvasInstance as Canvas3d).configureShapes({ opacity, diff --git a/cvat-ui/src/containers/annotation-page/canvas/canvas-wrapper3D.tsx b/cvat-ui/src/containers/annotation-page/canvas/canvas-wrapper3D.tsx index 12da11198f6f..c4587457587b 100644 --- a/cvat-ui/src/containers/annotation-page/canvas/canvas-wrapper3D.tsx +++ b/cvat-ui/src/containers/annotation-page/canvas/canvas-wrapper3D.tsx @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -20,60 +21,33 @@ import { } from 'actions/annotation-actions'; import { - ActiveControl, ColorBy, CombinedState, ContextMenuType, - GridColor, ObjectType, Workspace, } from 'reducers'; import { Canvas3d } from 'cvat-canvas3d-wrapper'; import { Canvas } from 'cvat-canvas-wrapper'; -import { KeyMap } from '../../../utils/mousetrap-react'; interface StateToProps { - canvasInstance: Canvas3d | Canvas; - jobInstance: any; - frameData: any; - curZLayer: number; - annotations: any[]; - sidebarCollapsed: boolean; - activatedStateID: number | null; - activatedAttributeID: number | null; - frameIssues: any[] | null; - frameAngle: number; - frameFetching: boolean; - frame: number; opacity: number; - colorBy: ColorBy; selectedOpacity: number; outlined: boolean; outlineColor: string; - showBitmap: boolean; - showProjections: boolean; - grid: boolean; - gridSize: number; - gridColor: GridColor; - gridOpacity: number; + colorBy: ColorBy; + frameFetching: boolean; + canvasInstance: Canvas3d | Canvas; + jobInstance: any; + frameData: any; + annotations: any[]; + contextMenuVisibility: boolean; activeLabelID: number; activeObjectType: ObjectType; - brightnessLevel: number; - contrastLevel: number; - saturationLevel: number; - resetZoom: boolean; - aamZoomMargin: number; - contextMenuVisibility: boolean; - showObjectsTextAlways: boolean; - showAllInterpolationTracks: boolean; workspace: Workspace; - minZLayer: number; - maxZLayer: number; - automaticBordering: boolean; - switchableAutomaticBordering: boolean; - keyMap: KeyMap; - canvasBackgroundColor: string; + frame: number; + resetZoom: boolean; } interface DispatchToProps { @@ -94,7 +68,6 @@ function mapStateToProps(state: CombinedState): StateToProps { const { annotation: { canvas: { - activeControl, instance: canvasInstance, contextMenu: { visible: contextMenuVisibility }, }, @@ -102,85 +75,39 @@ function mapStateToProps(state: CombinedState): StateToProps { job: { instance: jobInstance }, player: { frame: { data: frameData, number: frame, fetching: frameFetching }, - frameAngles, }, annotations: { states: annotations, - activatedStateID, - activatedAttributeID, - zLayer: { cur: curZLayer, min: minZLayer, max: maxZLayer }, }, - sidebarCollapsed, workspace, }, settings: { player: { - canvasBackgroundColor, - grid, - gridSize, - gridColor, - gridOpacity, - brightnessLevel, - contrastLevel, - saturationLevel, resetZoom, }, - workspace: { - aamZoomMargin, showObjectsTextAlways, showAllInterpolationTracks, automaticBordering, - }, shapes: { - opacity, colorBy, selectedOpacity, outlined, outlineColor, showBitmap, showProjections, + opacity, colorBy, selectedOpacity, outlined, outlineColor, }, }, - review: { frameIssues, issuesHidden }, - shortcuts: { keyMap }, } = state; return { canvasInstance, jobInstance, frameData, - curZLayer, contextMenuVisibility, annotations, - sidebarCollapsed, - frameIssues: - issuesHidden || ![Workspace.REVIEW_WORKSPACE, Workspace.STANDARD].includes(workspace) ? null : frameIssues, - frameAngle: frameAngles[frame - jobInstance.startFrame], frameFetching, frame, - activatedStateID, - activatedAttributeID, opacity, colorBy, selectedOpacity, outlined, outlineColor, - showBitmap, - showProjections, - grid, - gridSize, - gridColor, - gridOpacity, activeLabelID, activeObjectType, - brightnessLevel, - contrastLevel, - saturationLevel, resetZoom, - aamZoomMargin, - showObjectsTextAlways, - showAllInterpolationTracks, - minZLayer, - maxZLayer, - automaticBordering, workspace, - keyMap, - canvasBackgroundColor, - switchableAutomaticBordering: - activeControl === ActiveControl.DRAW_POLYGON || - activeControl === ActiveControl.DRAW_POLYLINE || - activeControl === ActiveControl.EDIT, }; } @@ -212,7 +139,7 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { dispatch(updateCanvasContextMenu(false, 0, 0)); } - dispatch(activateObject(activatedStateID, null)); + dispatch(activateObject(activatedStateID, null, null)); }, onEditShape(enabled: boolean): void { dispatch(editShape(enabled)); From e20685a225dec49d1de78f6073ece930aeaa7a65 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 30 Nov 2022 12:26:41 -0800 Subject: [PATCH 6/8] Updated changelog and version --- CHANGELOG.md | 1 + cvat-canvas3d/package.json | 2 +- cvat-ui/package.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc9e110e4e7d..84d5d7ed5e50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ from online detectors & interactors) ( - `api/docs`, `api/swagger`, `api/schema`, `server/about` endpoints now allow unauthorized access (, ) - Datumaro version is upgraded to 0.3 (dev) () - Allowed trailing slashes in the SDK host address () +- Adjusted initial camera position, enabled 'Reset zoom' option for 3D canvas () - Enabled authentication via email () ### Deprecated diff --git a/cvat-canvas3d/package.json b/cvat-canvas3d/package.json index 487e21decbcf..3f7ccb36ce98 100644 --- a/cvat-canvas3d/package.json +++ b/cvat-canvas3d/package.json @@ -1,6 +1,6 @@ { "name": "cvat-canvas3d", - "version": "0.0.1", + "version": "0.0.3", "description": "Part of Computer Vision Annotation Tool which presents its canvas3D library", "main": "src/canvas3d.ts", "scripts": { diff --git a/cvat-ui/package.json b/cvat-ui/package.json index 3220aa1c25de..c5200faa5f53 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.44.3", + "version": "1.44.4", "description": "CVAT single-page application", "main": "src/index.tsx", "scripts": { From 0d675768c160cc4b81178b09803b2088122a725d Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 1 Dec 2022 02:29:33 -0800 Subject: [PATCH 7/8] Fixed coordinates in one of tests --- ..._canvas3d_functionality_cuboid_grouping.js | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/tests/cypress/integration/canvas3d_functionality/case_83_canvas3d_functionality_cuboid_grouping.js b/tests/cypress/integration/canvas3d_functionality/case_83_canvas3d_functionality_cuboid_grouping.js index bd820d27fce2..324ab9b66da4 100644 --- a/tests/cypress/integration/canvas3d_functionality/case_83_canvas3d_functionality_cuboid_grouping.js +++ b/tests/cypress/integration/canvas3d_functionality/case_83_canvas3d_functionality_cuboid_grouping.js @@ -1,7 +1,10 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT +/* eslint-disable cypress/no-unnecessary-waiting */ + /// import { taskName, labelName } from '../../support/const_canvas3d'; @@ -10,24 +13,24 @@ context('Canvas 3D functionality. Grouping.', () => { const caseId = '83'; const screenshotsPath = 'cypress/screenshots/canvas3d_functionality/case_83_canvas3d_functionality_cuboid_grouping.js'; const firstCuboidCreationParams = { - labelName: labelName, + labelName, x: 480, - y: 160, + y: 150, }; const secondCuboidCreationParams = { - labelName: labelName, + labelName, x: 480, - y: 270, + y: 200, }; const thirdCuboidCreationParams = { - labelName: labelName, - x: 430, - y: 220, + labelName, + x: 530, + y: 150, }; const fourthCuboidCreationParams = { - labelName: labelName, + labelName, x: 530, - y: 220, + y: 200, }; const yellowHex = 'fcbe03'; const yellowRgb = '252, 190, 3'; @@ -48,7 +51,7 @@ context('Canvas 3D functionality. Grouping.', () => { } before(() => { - cy.openTask(taskName) + cy.openTask(taskName); cy.openJob(); cy.wait(1000); // Waiting for the point cloud to display cy.create3DCuboid(firstCuboidCreationParams); @@ -61,8 +64,8 @@ context('Canvas 3D functionality. Grouping.', () => { describe(`Testing case "${caseId}"`, () => { it('Grouping two cuboids.', () => { cy.get('.cvat-group-control').click(); - cy.get('.cvat-canvas3d-perspective').trigger('mousemove', 480, 270).click(480, 270); - cy.get('.cvat-canvas3d-perspective').trigger('mousemove', 430, 220).click(430, 220); + cy.get('.cvat-canvas3d-perspective').trigger('mousemove', 480, 200).click(480, 200); + cy.get('.cvat-canvas3d-perspective').trigger('mousemove', 530, 150).click(530, 150); cy.get('.cvat-group-control').click(); cy.changeAppearance('Group'); cy.get('#cvat-objects-sidebar-state-item-1').invoke('attr', 'style').then((bgColorItem1) => { @@ -102,8 +105,8 @@ context('Canvas 3D functionality. Grouping.', () => { it('Reset group.', () => { cy.customScreenshot('.cvat-canvas3d-perspective', 'canvas3d_perspective_before_reset_group'); cy.get('.cvat-group-control').click(); - cy.get('.cvat-canvas3d-perspective').trigger('mousemove', 480, 270).click(480, 270); - cy.get('.cvat-canvas3d-perspective').trigger('mousemove', 430, 220).click(430, 220); + cy.get('.cvat-canvas3d-perspective').trigger('mousemove', 480, 200).click(480, 200); + cy.get('.cvat-canvas3d-perspective').trigger('mousemove', 530, 150).click(530, 150); cy.get('body').type('{Shift}g'); cy.get('#cvat-objects-sidebar-state-item-2').invoke('attr', 'style').then((bgColorItem2) => { expect(bgColorItem).to.be.equal(bgColorItem2); From 35b5a33e963927aea23e6cdbb84c15ce1e2af3ca Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 1 Dec 2022 02:53:02 -0800 Subject: [PATCH 8/8] Tried to fix one more test --- ..._functionality_cuboid_opacity_outlined_borders.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/cypress/integration/canvas3d_functionality_2/case_82_canvas3d_functionality_cuboid_opacity_outlined_borders.js b/tests/cypress/integration/canvas3d_functionality_2/case_82_canvas3d_functionality_cuboid_opacity_outlined_borders.js index db7a6b3b1eb9..ffe705dddc07 100644 --- a/tests/cypress/integration/canvas3d_functionality_2/case_82_canvas3d_functionality_cuboid_opacity_outlined_borders.js +++ b/tests/cypress/integration/canvas3d_functionality_2/case_82_canvas3d_functionality_cuboid_opacity_outlined_borders.js @@ -1,7 +1,10 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT +/* eslint-disable cypress/no-unnecessary-waiting */ + /// import { taskName, labelName } from '../../support/const_canvas3d'; @@ -10,13 +13,13 @@ context('Canvas 3D functionality. Opacity. Outlined borders.', () => { const caseId = '82'; const screenshotsPath = 'cypress/screenshots/canvas3d_functionality_2/case_82_canvas3d_functionality_cuboid_opacity_outlined_borders.js'; const cuboidCreationParams = { - labelName: labelName, + labelName, x: 500, y: 250, }; before(() => { - cy.openTask(taskName) + cy.openTask(taskName); cy.openJob(); cy.wait(1000); // Waiting for the point cloud to display cy.create3DCuboid(cuboidCreationParams); @@ -64,6 +67,11 @@ context('Canvas 3D functionality. Opacity. Outlined borders.', () => { it('Enable/disable outlined borders.', () => { cy.get('.cvat-appearance-outlinded-borders-checkbox').find('[type="checkbox"]').check().should('be.checked'); + cy.get('.cvat-appearance-outlined-borders-button').click(); + cy.get('.cvat-label-color-picker').should('exist').and('be.visible').within(() => { + cy.get('div[title="#ff007c"]').click(); + cy.contains('Ok').click(); + }); cy.customScreenshot('.cvat-canvas3d-perspective', 'canvas3d_perspective_enable_outlined_borders'); cy.compareImagesAndCheckResult( `${screenshotsPath}/canvas3d_perspective_enable_outlined_borders.png`,