From 080755a8da2161684d342b8694002bfd9db13781 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 9 Nov 2022 05:01:39 -0800 Subject: [PATCH] Fixed issue 5256, improved 'occluded' visualization (#5259) * Fixed issue 5256, improved 'occluded' visualization * Updated version & added test * Updated changelog * Fixed tests --- CHANGELOG.md | 1 + cvat-canvas/package.json | 2 +- cvat-canvas/src/scss/canvas.scss | 13 +++++++++++++ cvat-canvas/src/typescript/canvasView.ts | 9 +++++++-- cvat-ui/src/actions/annotation-actions.ts | 2 +- .../objects-side-bar/objects-list.tsx | 6 +++--- tests/cypress/integration/masks/masks_basics.js | 2 +- .../integration/skeletons/skeletons_pipeline.js | 10 ++++++++-- 8 files changed, 35 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ebdc34e0859..1ba6b8a811b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,7 @@ non-ascii paths while adding files from "Connected file share" (issue #4428) - Job assignee can not resolve an issue () - Create manifest with cvat/server docker container command () - Cannot assign a resource to a user who has an organization () +- Occluded not applied on canvas instantly for a skeleton elements () - Oriented bounding boxes broken with COCO format ss() - Fixed upload resumption in production environments () diff --git a/cvat-canvas/package.json b/cvat-canvas/package.json index eb1aa7d72815..b97d416139f0 100644 --- a/cvat-canvas/package.json +++ b/cvat-canvas/package.json @@ -1,6 +1,6 @@ { "name": "cvat-canvas", - "version": "2.16.0", + "version": "2.16.1", "description": "Part of Computer Vision Annotation Tool which presents its canvas library", "main": "src/canvas.ts", "scripts": { diff --git a/cvat-canvas/src/scss/canvas.scss b/cvat-canvas/src/scss/canvas.scss index c90d8b819633..556ba4a337de 100644 --- a/cvat-canvas/src/scss/canvas.scss +++ b/cvat-canvas/src/scss/canvas.scss @@ -148,10 +148,23 @@ polyline.cvat_canvas_shape_splitting { stroke-dasharray: 5; } +.cvat_canvas_shape_occluded_point { + stroke-dasharray: 1 !important; + stroke: white; +} + +circle.cvat_canvas_shape_occluded { + @extend .cvat_canvas_shape_occluded_point; +} + g.cvat_canvas_shape_occluded { > rect { stroke-dasharray: 5; } + + > circle { + @extend .cvat_canvas_shape_occluded_point; + } } .svg_select_points_rot { diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 1de41214e675..1eeafbf1ee59 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -1916,10 +1916,11 @@ export class CanvasViewImpl implements CanvasView, Listener { } if (drawnState.occluded !== state.occluded) { + const instance = state.shapeType === 'points' ? this.svgShapes[clientID].remember('_selectHandler').nested : shape; if (state.occluded) { - shape.addClass('cvat_canvas_shape_occluded'); + instance.addClass('cvat_canvas_shape_occluded'); } else { - shape.removeClass('cvat_canvas_shape_occluded'); + instance.removeClass('cvat_canvas_shape_occluded'); } } @@ -3388,6 +3389,10 @@ export class CanvasViewImpl implements CanvasView, Listener { group.addClass('cvat_canvas_hidden'); } + if (state.occluded) { + group.addClass('cvat_canvas_shape_occluded'); + } + shape.remove = (): SVG.PolyLine => { this.selectize(false, shape); shape.constructor.prototype.remove.call(shape); diff --git a/cvat-ui/src/actions/annotation-actions.ts b/cvat-ui/src/actions/annotation-actions.ts index fa5106aeaef9..8816760344ec 100644 --- a/cvat-ui/src/actions/annotation-actions.ts +++ b/cvat-ui/src/actions/annotation-actions.ts @@ -1223,7 +1223,7 @@ export function updateAnnotationsAsync(statesToUpdate: any[]): ThunkAction { const states = await Promise.all(promises); const needToUpdateAll = states - .some((state: any) => [ShapeType.MASK, ShapeType.SKELETON].includes(state.shapeType)); + .some((state: any) => state.shapeType === ShapeType.MASK || state.parentID !== null); if (needToUpdateAll) { dispatch(fetchAnnotationsAsync()); return; diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/objects-list.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/objects-list.tsx index b2eb399a7b8a..bd9b103f13d1 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/objects-list.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/objects-list.tsx @@ -319,12 +319,12 @@ class ObjectsListContainer extends React.PureComponent { } }; - const activatedState = (): ObjectState | null => { + const activatedState = (ignoreElements = false): ObjectState | null => { if (activatedStateID !== null) { const state = objectStates .find((objectState: ObjectState): boolean => objectState.clientID === activatedStateID); - if (state && activatedElementID !== null) { + if (state && activatedElementID !== null && !ignoreElements) { const element = state.elements .find((_element: ObjectState): boolean => _element.clientID === activatedElementID); return element || null; @@ -399,7 +399,7 @@ class ObjectsListContainer extends React.PureComponent { }, DELETE_OBJECT: (event: KeyboardEvent | undefined) => { preventDefault(event); - const state = activatedState(); + const state = activatedState(true); if (state && !readonly) { removeObject(state, event ? event.shiftKey : false); } diff --git a/tests/cypress/integration/masks/masks_basics.js b/tests/cypress/integration/masks/masks_basics.js index 19ce61799122..66081dc3cb26 100644 --- a/tests/cypress/integration/masks/masks_basics.js +++ b/tests/cypress/integration/masks/masks_basics.js @@ -4,7 +4,7 @@ /// -context('Manipulations with masks', () => { +context('Manipulations with masks', { scrollBehavior: false }, () => { const taskName = 'Basic actions with masks'; const serverFiles = ['images/image_1.jpg', 'images/image_2.jpg', 'images/image_3.jpg']; const drawingActions = [{ diff --git a/tests/cypress/integration/skeletons/skeletons_pipeline.js b/tests/cypress/integration/skeletons/skeletons_pipeline.js index 21be2f0ef392..de1aaa73f53e 100644 --- a/tests/cypress/integration/skeletons/skeletons_pipeline.js +++ b/tests/cypress/integration/skeletons/skeletons_pipeline.js @@ -4,7 +4,7 @@ /// -context('Manipulations with skeletons', () => { +context('Manipulations with skeletons', { scrollBehavior: false }, () => { const skeletonSize = 5; const labelName = 'skeleton'; const taskName = 'skeletons main pipeline'; @@ -169,8 +169,14 @@ context('Manipulations with skeletons', () => { cy.get(selector).should('not.exist'); } - it('Creating and removing a skeleton shape', () => { + it('Creating, checking occluded for a single point, and removing a skeleton shape', () => { createSkeletonObject('shape'); + + cy.get('#cvat-objects-sidebar-state-item-element-2').within(() => { + cy.get('span[aria-label="user"]').click(); + }); + cy.get('#cvat_canvas_shape_2').should('have.class', 'cvat_canvas_shape_occluded'); + deleteSkeleton('#cvat_canvas_shape_1', 'shape', false); cy.removeAnnotations(); });