From 9a66509ba8ada71b1207a100c798a7d1c82c5bbc Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 5 Feb 2025 20:17:21 +0200 Subject: [PATCH] Fixed: In some cases effect of drag/resize may be reset implicitly for a user (#9053) --- ...04118_sekachev.bs_fixed_implicit_change.md | 4 +++ cvat-canvas/src/typescript/canvasView.ts | 26 +++++++++++++++++++ cvat-canvas/src/typescript/consts.ts | 5 ++-- cvat-canvas/src/typescript/drawHandler.ts | 22 ++++++++++------ cvat-canvas/src/typescript/shared.ts | 2 +- cvat-core/src/object-utils.ts | 8 +++--- .../settings-modal/workspace-settings.tsx | 4 +-- 7 files changed, 53 insertions(+), 18 deletions(-) create mode 100644 changelog.d/20250205_104118_sekachev.bs_fixed_implicit_change.md diff --git a/changelog.d/20250205_104118_sekachev.bs_fixed_implicit_change.md b/changelog.d/20250205_104118_sekachev.bs_fixed_implicit_change.md new file mode 100644 index 000000000000..5df46a57f9a4 --- /dev/null +++ b/changelog.d/20250205_104118_sekachev.bs_fixed_implicit_change.md @@ -0,0 +1,4 @@ +### Fixed + +- In some cases effect of drag/resize may be reset implicitly for a user + () diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 60d51c369aff..a777cec43fc7 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -404,6 +404,32 @@ export class CanvasViewImpl implements CanvasView, Listener { this.canvas.style.cursor = ''; this.mode = Mode.IDLE; if (state && points) { + // we need to store "updated" and set "points" to an empty array + // as this information is used to define "updated" objects in diff logic during canvas objects setup + // if because of any reason updating was actually rejected somewhere, we must reset view inside this logic + + // there is one more deeper issue: + // somewhere canvas updates drawn views and then sends request, + // updating internal CVAT state (e.g. drag, resize) + // somewhere, however, it just sends request to update internal CVAT state + // (e.g. remove point, edit polygon/polyline) + // if object view was not changed by canvas and points accepted as is without any changes + // the view will not be updated during objects setup if we just set points as is here + // that is why we need to set points to an empty array (something that can't normally come from CVAT) + // I do not think it can be easily fixed now, hovewer in the future we should refactor code + if (Number.isInteger(state.parentID)) { + const { elements } = this.drawnStates[state.parentID]; + const drawnElement = elements.find((el) => el.clientID === state.clientID); + drawnElement.updated = Date.now(); + drawnElement.points = []; + + this.drawnStates[state.parentID].updated = drawnElement.updated; + this.drawnStates[state.parentID].points = []; + } else { + this.drawnStates[state.clientID].updated = Date.now(); + this.drawnStates[state.clientID].points = []; + } + const event: CustomEvent = new CustomEvent('canvas.edited', { bubbles: false, cancelable: true, diff --git a/cvat-canvas/src/typescript/consts.ts b/cvat-canvas/src/typescript/consts.ts index 3ea75dbb557d..1e39c1316879 100644 --- a/cvat-canvas/src/typescript/consts.ts +++ b/cvat-canvas/src/typescript/consts.ts @@ -1,4 +1,5 @@ // Copyright (C) 2019-2022 Intel Corporation +// Copyright (C) CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -6,8 +7,7 @@ const BASE_STROKE_WIDTH = 1.25; const BASE_GRID_WIDTH = 2; const BASE_POINT_SIZE = 4; const TEXT_MARGIN = 10; -const AREA_THRESHOLD = 9; -const SIZE_THRESHOLD = 3; +const SIZE_THRESHOLD = 1; const POINTS_STROKE_WIDTH = 1; const POINTS_SELECTED_STROKE_WIDTH = 4; const MIN_EDGE_LENGTH = 3; @@ -36,7 +36,6 @@ export default { BASE_GRID_WIDTH, BASE_POINT_SIZE, TEXT_MARGIN, - AREA_THRESHOLD, SIZE_THRESHOLD, POINTS_STROKE_WIDTH, POINTS_SELECTED_STROKE_WIDTH, diff --git a/cvat-canvas/src/typescript/drawHandler.ts b/cvat-canvas/src/typescript/drawHandler.ts index d54117c72957..ebee54109a04 100644 --- a/cvat-canvas/src/typescript/drawHandler.ts +++ b/cvat-canvas/src/typescript/drawHandler.ts @@ -47,16 +47,18 @@ interface FinalCoordinates { function checkConstraint(shapeType: string, points: number[], box: Box | null = null): boolean { if (shapeType === 'rectangle') { const [xtl, ytl, xbr, ybr] = points; - return (xbr - xtl) * (ybr - ytl) >= consts.AREA_THRESHOLD; + const [width, height] = [xbr - xtl, ybr - ytl]; + return width >= consts.SIZE_THRESHOLD && height >= consts.SIZE_THRESHOLD; } if (shapeType === 'polygon') { - return (box.xbr - box.xtl) * (box.ybr - box.ytl) >= consts.AREA_THRESHOLD && points.length >= 3 * 2; + const [width, height] = [box.xbr - box.xtl, box.ybr - box.ytl]; + return (width >= consts.SIZE_THRESHOLD || height > consts.SIZE_THRESHOLD) && points.length >= 3 * 2; } if (shapeType === 'polyline') { - return (box.xbr - box.xtl >= consts.SIZE_THRESHOLD || - box.ybr - box.ytl >= consts.SIZE_THRESHOLD) && points.length >= 2 * 2; + const [width, height] = [box.xbr - box.xtl, box.ybr - box.ytl]; + return (width >= consts.SIZE_THRESHOLD || height >= consts.SIZE_THRESHOLD) && points.length >= 2 * 2; } if (shapeType === 'points') { @@ -64,18 +66,22 @@ function checkConstraint(shapeType: string, points: number[], box: Box | null = } if (shapeType === 'ellipse') { - const [rx, ry] = [points[2] - points[0], points[1] - points[3]]; - return rx * ry * Math.PI >= consts.AREA_THRESHOLD; + const [width, height] = [(points[2] - points[0]) * 2, (points[1] - points[3]) * 2]; + return width >= consts.SIZE_THRESHOLD && height > consts.SIZE_THRESHOLD; } if (shapeType === 'cuboid') { return points.length === 4 * 2 || points.length === 8 * 2 || - (points.length === 2 * 2 && (points[2] - points[0]) * (points[3] - points[1]) >= consts.AREA_THRESHOLD); + (points.length === 2 * 2 && + (points[2] - points[0]) >= consts.SIZE_THRESHOLD && + (points[3] - points[1]) >= consts.SIZE_THRESHOLD + ); } if (shapeType === 'skeleton') { const [xtl, ytl, xbr, ybr] = points; - return (xbr - xtl >= 1 || ybr - ytl >= 1); + const [width, height] = [xbr - xtl, ybr - ytl]; + return width >= consts.SIZE_THRESHOLD || height >= consts.SIZE_THRESHOLD; } return false; diff --git a/cvat-canvas/src/typescript/shared.ts b/cvat-canvas/src/typescript/shared.ts index 9e210067e7d7..bde8cdbb8671 100644 --- a/cvat-canvas/src/typescript/shared.ts +++ b/cvat-canvas/src/typescript/shared.ts @@ -100,7 +100,7 @@ export function displayShapeSize(shapesContainer: SVG.Container, textContainer: .fill('white') .addClass('cvat_canvas_text'), update(shape: SVG.Shape): void { - let text = `${Math.round(shape.width())}x${Math.round(shape.height())}px`; + let text = `${Math.floor(shape.width())}x${Math.floor(shape.height())}px`; if (shape.type === 'rect' || shape.type === 'ellipse') { let rotation = shape.transform().rotation || 0; // be sure, that rotation in range [0; 360] diff --git a/cvat-core/src/object-utils.ts b/cvat-core/src/object-utils.ts index 000d169de847..06a90a958924 100644 --- a/cvat-core/src/object-utils.ts +++ b/cvat-core/src/object-utils.ts @@ -99,10 +99,10 @@ export function checkShapeArea(shapeType: ShapeType, points: number[]): boolean ymax = Math.max(ymax, points[i + 1]); } - if (shapeType === ShapeType.POLYLINE) { - // horizontal / vertical lines have one of dimensions equal to zero - const length = Math.max(xmax - xmin, ymax - ymin); - return length >= MIN_SHAPE_SIZE; + if ([ShapeType.POLYLINE, ShapeType.SKELETON, ShapeType.POLYGON].includes(shapeType)) { + // for polyshapes consider at least one dimension + // skeleton in corner cases may be a regular polyshape + return Math.max(xmax - xmin, ymax - ymin) >= MIN_SHAPE_SIZE; } [width, height] = [xmax - xmin, ymax - ymin]; diff --git a/cvat-ui/src/components/header/settings-modal/workspace-settings.tsx b/cvat-ui/src/components/header/settings-modal/workspace-settings.tsx index e624e7c4adf0..c659daa8ac0e 100644 --- a/cvat-ui/src/components/header/settings-modal/workspace-settings.tsx +++ b/cvat-ui/src/components/header/settings-modal/workspace-settings.tsx @@ -80,8 +80,8 @@ function WorkspaceSettingsComponent(props: Props): JSX.Element { const maxAutoSaveInterval = 60; const minAAMMargin = 0; const maxAAMMargin = 1000; - const minControlPointsSize = 4; - const maxControlPointsSize = 8; + const minControlPointsSize = 2; + const maxControlPointsSize = 10; return (