diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d7c688ec703..d13b52edae42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,9 +57,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Uncaught TypeError: this.el.node.getScreenCTM() is null in Firefox () - Bug: canvas is busy when start playing, start resizing a shape and do not release the mouse cursor () - Fixed tus upload error over https () +- Issues disappear when rescale a browser () - Auth token key is not returned when registering without email verification () - ### Security - Updated ELK to 6.8.22 which uses log4j 2.17.0 () diff --git a/cvat-canvas/README.md b/cvat-canvas/README.md index c4334ad490fa..68939171a625 100644 --- a/cvat-canvas/README.md +++ b/cvat-canvas/README.md @@ -184,6 +184,7 @@ Standard JS events are used. - canvas.zoomstart - canvas.zoomstop - canvas.zoom + - canvas.reshape - canvas.fit - canvas.dragshape => {id: number} - canvas.roiselected => {points: number[]} diff --git a/cvat-canvas/package-lock.json b/cvat-canvas/package-lock.json index 2b4aaa0da08e..4ae904c9ed48 100644 --- a/cvat-canvas/package-lock.json +++ b/cvat-canvas/package-lock.json @@ -1,12 +1,12 @@ { "name": "cvat-canvas", - "version": "2.12.2", + "version": "2.13.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "cvat-canvas", - "version": "2.12.2", + "version": "2.13.0", "license": "MIT", "dependencies": { "@types/polylabel": "^1.0.5", diff --git a/cvat-canvas/package.json b/cvat-canvas/package.json index c348438b1034..45df72511bde 100644 --- a/cvat-canvas/package.json +++ b/cvat-canvas/package.json @@ -1,6 +1,6 @@ { "name": "cvat-canvas", - "version": "2.12.2", + "version": "2.13.0", "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 20fb2c69ccb3..273010568143 100644 --- a/cvat-canvas/src/scss/canvas.scss +++ b/cvat-canvas/src/scss/canvas.scss @@ -74,7 +74,6 @@ polyline.cvat_shape_drawing_opacity { } .cvat_canvas_issue_region { - display: none; stroke-width: 0; } diff --git a/cvat-canvas/src/typescript/canvas.ts b/cvat-canvas/src/typescript/canvas.ts index 3ccbbaa1d0fc..aec02854a446 100644 --- a/cvat-canvas/src/typescript/canvas.ts +++ b/cvat-canvas/src/typescript/canvas.ts @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021 Intel Corporation +// Copyright (C) 2019-2022 Intel Corporation // // SPDX-License-Identifier: MIT @@ -29,7 +29,7 @@ const CanvasVersion = pjson.version; interface Canvas { html(): HTMLDivElement; setup(frameData: any, objectStates: any[], zLayer?: number): void; - setupIssueRegions(issueRegions: Record): void; + setupIssueRegions(issueRegions: Record): void; activate(clientID: number | null, attributeID?: number): void; rotate(rotationAngle: number): void; focus(clientID: number, padding?: number): void; @@ -77,7 +77,7 @@ class CanvasImpl implements Canvas { this.model.setup(frameData, objectStates, zLayer); } - public setupIssueRegions(issueRegions: Record): void { + public setupIssueRegions(issueRegions: Record): void { this.model.setupIssueRegions(issueRegions); } diff --git a/cvat-canvas/src/typescript/canvasController.ts b/cvat-canvas/src/typescript/canvasController.ts index dca3c7d888b0..7ec577f57808 100644 --- a/cvat-canvas/src/typescript/canvasController.ts +++ b/cvat-canvas/src/typescript/canvasController.ts @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2020 Intel Corporation +// Copyright (C) 2019-2022 Intel Corporation // // SPDX-License-Identifier: MIT @@ -19,7 +19,7 @@ import { export interface CanvasController { readonly objects: any[]; - readonly issueRegions: Record; + readonly issueRegions: Record; readonly zLayer: number | null; readonly focusData: FocusData; readonly activeElement: ActiveElement; @@ -123,7 +123,7 @@ export class CanvasControllerImpl implements CanvasController { return this.model.zLayer; } - public get issueRegions(): Record { + public get issueRegions(): Record { return this.model.issueRegions; } diff --git a/cvat-canvas/src/typescript/canvasModel.ts b/cvat-canvas/src/typescript/canvasModel.ts index bb391f582bd6..bc43af8fec42 100644 --- a/cvat-canvas/src/typescript/canvasModel.ts +++ b/cvat-canvas/src/typescript/canvasModel.ts @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2021 Intel Corporation +// Copyright (C) 2019-2022 Intel Corporation // // SPDX-License-Identifier: MIT @@ -172,7 +172,7 @@ export enum Mode { export interface CanvasModel { readonly imageBitmap: boolean; readonly image: Image | null; - readonly issueRegions: Record; + readonly issueRegions: Record; readonly objects: any[]; readonly zLayer: number | null; readonly gridSize: Size; @@ -193,7 +193,7 @@ export interface CanvasModel { move(topOffset: number, leftOffset: number): void; setup(frameData: any, objectStates: any[], zLayer: number): void; - setupIssueRegions(issueRegions: Record): void; + setupIssueRegions(issueRegions: Record): void; activate(clientID: number | null, attributeID: number | null): void; rotate(rotationAngle: number): void; focus(clientID: number, padding: number): void; @@ -234,7 +234,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { gridSize: Size; left: number; objects: any[]; - issueRegions: Record; + issueRegions: Record; scale: number; top: number; zLayer: number | null; @@ -353,6 +353,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { this.notify(UpdateReasons.FITTED_CANVAS); this.notify(UpdateReasons.OBJECTS_UPDATED); + this.notify(UpdateReasons.ISSUE_REGIONS_UPDATED); } public bitmap(enabled: boolean): void { @@ -445,7 +446,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { }); } - public setupIssueRegions(issueRegions: Record): void { + public setupIssueRegions(issueRegions: Record): void { this.data.issueRegions = issueRegions; this.notify(UpdateReasons.ISSUE_REGIONS_UPDATED); } @@ -756,7 +757,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { return this.data.image; } - public get issueRegions(): Record { + public get issueRegions(): Record { return { ...this.data.issueRegions }; } diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 4da2e35ac6c7..7aad5789e109 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -606,7 +606,7 @@ export class CanvasViewImpl implements CanvasView, Listener { } } - private setupIssueRegions(issueRegions: Record): void { + private setupIssueRegions(issueRegions: Record): void { for (const issueRegion of Object.keys(this.drawnIssueRegions)) { if (!(issueRegion in issueRegions) || !+issueRegion) { this.drawnIssueRegions[+issueRegion].remove(); @@ -616,7 +616,7 @@ export class CanvasViewImpl implements CanvasView, Listener { for (const issueRegion of Object.keys(issueRegions)) { if (issueRegion in this.drawnIssueRegions) continue; - const points = this.translateToCanvas(issueRegions[+issueRegion]); + const points = this.translateToCanvas(issueRegions[+issueRegion].points); if (points.length === 2) { this.drawnIssueRegions[+issueRegion] = this.adoptedContent .circle((consts.BASE_POINT_SIZE * 3 * 2) / this.geometry.scale) @@ -656,6 +656,10 @@ export class CanvasViewImpl implements CanvasView, Listener { 'stroke-width': `${consts.BASE_STROKE_WIDTH / this.geometry.scale}`, }); } + + if (issueRegions[+issueRegion].hidden) { + this.drawnIssueRegions[+issueRegion].style({ display: 'none' }); + } } } @@ -1263,8 +1267,15 @@ export class CanvasViewImpl implements CanvasView, Listener { } else if (reason === UpdateReasons.FITTED_CANVAS) { // Canvas geometry is going to be changed. Old object positions aren't valid any more this.setupObjects([]); + this.setupIssueRegions({}); this.moveCanvas(); this.resizeCanvas(); + this.canvas.dispatchEvent( + new CustomEvent('canvas.reshape', { + bubbles: false, + cancelable: true, + }), + ); } else if ([UpdateReasons.IMAGE_ZOOMED, UpdateReasons.IMAGE_FITTED].includes(reason)) { this.moveCanvas(); this.transformCanvas(); diff --git a/cvat-ui/package-lock.json b/cvat-ui/package-lock.json index e37867502cf1..ab225266889e 100644 --- a/cvat-ui/package-lock.json +++ b/cvat-ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "cvat-ui", - "version": "1.33.2", + "version": "1.33.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "cvat-ui", - "version": "1.33.2", + "version": "1.33.3", "license": "MIT", "dependencies": { "@ant-design/icons": "^4.6.3", diff --git a/cvat-ui/package.json b/cvat-ui/package.json index 02e2f6b9caa5..e130cd8c6e6e 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.33.2", + "version": "1.33.3", "description": "CVAT single-page application", "main": "src/index.tsx", "scripts": { diff --git a/cvat-ui/src/components/annotation-page/canvas/canvas-context-menu.tsx b/cvat-ui/src/components/annotation-page/canvas/canvas-context-menu.tsx index 8b25daed6fbf..9cf44456f9eb 100644 --- a/cvat-ui/src/components/annotation-page/canvas/canvas-context-menu.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/canvas-context-menu.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2021 Intel Corporation +// Copyright (C) 2021-2022 Intel Corporation // // SPDX-License-Identifier: MIT @@ -10,6 +10,7 @@ import { MenuInfo } from 'rc-menu/lib/interface'; import ObjectItemContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/object-item'; import { Workspace } from 'reducers/interfaces'; +import { rotatePoint } from 'utils/math'; import consts from 'consts'; interface Props { @@ -106,7 +107,29 @@ export default function CanvasContextMenu(props: Props): JSX.Element | null { ); if (param.key === ReviewContextMenuKeys.OPEN_ISSUE) { if (state) { - onStartIssue(state.points); + let { points } = state; + if (['ellipse', 'rectangle'].includes(state.shapeType)) { + const [cx, cy] = state.shapeType === 'ellipse' ? state.points : [ + (state.points[0] + state.points[2]) / 2, + (state.points[1] + state.points[3]) / 2, + ]; + const [rx, ry] = [state.points[2] - cx, cy - state.points[3]]; + points = state.shapeType === 'ellipse' ? [ + state.points[0] - rx, + state.points[1] - ry, + state.points[0] + rx, + state.points[1] + ry, + ] : state.points; + + points = [ + [points[0], points[1]], + [points[2], points[1]], + [points[2], points[3]], + [points[0], points[3]], + ].map(([x, y]: number[]) => rotatePoint(x, y, state.rotation, cx, cy)).flat(); + } + + onStartIssue(points); } } else if (param.key === ReviewContextMenuKeys.QUICK_ISSUE_POSITION) { if (state) { diff --git a/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx b/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx index 6c1ca79b7f07..6b1e8fbae13d 100644 --- a/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper.tsx @@ -32,7 +32,6 @@ interface Props { activatedStateID: number | null; activatedAttributeID: number | null; annotations: any[]; - frameIssues: any[] | null; frameData: any; frameAngle: number; frameFetching: boolean; @@ -136,7 +135,6 @@ export default class CanvasWrapperComponent extends React.PureComponent { }); this.initialSetup(); - this.updateIssueRegions(); this.updateCanvas(); } @@ -148,7 +146,6 @@ export default class CanvasWrapperComponent extends React.PureComponent { outlined, outlineColor, showBitmap, - frameIssues, frameData, frameAngle, annotations, @@ -256,10 +253,6 @@ export default class CanvasWrapperComponent extends React.PureComponent { } } - if (prevProps.frameIssues !== frameIssues) { - this.updateIssueRegions(); - } - if ( prevProps.annotations !== annotations || prevProps.frameData !== frameData || @@ -684,23 +677,6 @@ export default class CanvasWrapperComponent extends React.PureComponent { } } - private updateIssueRegions(): void { - const { frameIssues } = this.props; - const { canvasInstance } = this.props as { canvasInstance: Canvas }; - if (frameIssues === null) { - canvasInstance.setupIssueRegions({}); - } else { - const regions = frameIssues.reduce((acc: Record, issue: any): Record< - number, - number[] - > => { - acc[issue.id] = issue.position; - return acc; - }, {}); - canvasInstance.setupIssueRegions(regions); - } - } - private updateCanvas(): void { const { curZLayer, annotations, frameData, canvasInstance, diff --git a/cvat-ui/src/components/annotation-page/review-workspace/controls-side-bar/issue-control.tsx b/cvat-ui/src/components/annotation-page/review-workspace/controls-side-bar/issue-control.tsx index 4947f7661518..8448b16a84bc 100644 --- a/cvat-ui/src/components/annotation-page/review-workspace/controls-side-bar/issue-control.tsx +++ b/cvat-ui/src/components/annotation-page/review-workspace/controls-side-bar/issue-control.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2021 Intel Corporation +// Copyright (C) 2020-2022 Intel Corporation // // SPDX-License-Identifier: MIT @@ -16,7 +16,7 @@ interface Props { selectIssuePosition(enabled: boolean): void; } -function ResizeControl(props: Props): JSX.Element { +function CreateIssueControl(props: Props): JSX.Element { const { activeControl, canvasInstance, selectIssuePosition } = props; return ( @@ -43,4 +43,4 @@ function ResizeControl(props: Props): JSX.Element { ); } -export default React.memo(ResizeControl); +export default React.memo(CreateIssueControl); diff --git a/cvat-ui/src/components/annotation-page/review/create-issue-dialog.tsx b/cvat-ui/src/components/annotation-page/review/create-issue-dialog.tsx index fcd415dafe5f..50b14058df17 100644 --- a/cvat-ui/src/components/annotation-page/review/create-issue-dialog.tsx +++ b/cvat-ui/src/components/annotation-page/review/create-issue-dialog.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2020-2022 Intel Corporation // // SPDX-License-Identifier: MIT @@ -16,13 +16,15 @@ import { Store } from 'antd/lib/form/interface'; interface FormProps { top: number; left: number; + angle: number; + scale: number; submit(message: string): void; cancel(): void; } function MessageForm(props: FormProps): JSX.Element { const { - top, left, submit, cancel, + top, left, angle, scale, submit, cancel, } = props; function handleSubmit(values: Store): void { @@ -30,7 +32,11 @@ function MessageForm(props: FormProps): JSX.Element { } return ( -
+ handleSubmit(values)} + > @@ -53,16 +59,22 @@ function MessageForm(props: FormProps): JSX.Element { interface Props { top: number; left: number; + angle: number; + scale: number; } export default function CreateIssueDialog(props: Props): ReactPortal { const dispatch = useDispatch(); - const { top, left } = props; + const { + top, left, angle, scale, + } = props; return ReactDOM.createPortal( { dispatch(finishIssueAsync(message)); }} diff --git a/cvat-ui/src/components/annotation-page/review/hidden-issue-label.tsx b/cvat-ui/src/components/annotation-page/review/hidden-issue-label.tsx index 1be37564d951..c9c4b18c09bb 100644 --- a/cvat-ui/src/components/annotation-page/review/hidden-issue-label.tsx +++ b/cvat-ui/src/components/annotation-page/review/hidden-issue-label.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2021 Intel Corporation +// Copyright (C) 2020-2022 Intel Corporation // // SPDX-License-Identifier: MIT @@ -14,6 +14,8 @@ interface Props { message: string; top: number; left: number; + angle: number; + scale: number; resolved: boolean; onClick: () => void; highlight: () => void; @@ -22,7 +24,7 @@ interface Props { export default function HiddenIssueLabel(props: Props): ReactPortal { const { - id, message, top, left, resolved, onClick, highlight, blur, + id, message, top, left, angle, scale, resolved, onClick, highlight, blur, } = props; const ref = useRef(null); @@ -54,7 +56,7 @@ export default function HiddenIssueLabel(props: Props): ReactPortal { } } }} - style={{ top, left }} + style={{ top, left, transform: `scale(${scale}) rotate(${angle}deg)` }} className='cvat-hidden-issue-label' > {resolved ? ( diff --git a/cvat-ui/src/components/annotation-page/review/issue-dialog.tsx b/cvat-ui/src/components/annotation-page/review/issue-dialog.tsx index d8b96c26f938..e5d72c1bf8ac 100644 --- a/cvat-ui/src/components/annotation-page/review/issue-dialog.tsx +++ b/cvat-ui/src/components/annotation-page/review/issue-dialog.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2021 Intel Corporation +// Copyright (C) 2020-2022 Intel Corporation // // SPDX-License-Identifier: MIT @@ -29,6 +29,8 @@ interface Props { top: number; resolved: boolean; isFetching: boolean; + angle: number; + scale: number; collapse: () => void; resolve: () => void; reopen: () => void; @@ -46,6 +48,8 @@ export default function IssueDialog(props: Props): JSX.Element { id, left, top, + scale, + angle, resolved, isFetching, collapse, @@ -112,7 +116,7 @@ export default function IssueDialog(props: Props): JSX.Element { ); return ReactDOM.createPortal( -
+
{id >= 0 ? `Issue #${id}` : 'Issue'} diff --git a/cvat-ui/src/components/annotation-page/review/issues-aggregator.tsx b/cvat-ui/src/components/annotation-page/review/issues-aggregator.tsx index 4507927c128b..019c233b00ff 100644 --- a/cvat-ui/src/components/annotation-page/review/issues-aggregator.tsx +++ b/cvat-ui/src/components/annotation-page/review/issues-aggregator.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2021 Intel Corporation +// Copyright (C) 2020-2022 Intel Corporation // // SPDX-License-Identifier: MIT @@ -15,73 +15,80 @@ import CreateIssueDialog from './create-issue-dialog'; import HiddenIssueLabel from './hidden-issue-label'; import IssueDialog from './issue-dialog'; -const scaleHandler = (canvasInstance: Canvas): void => { - const { geometry } = canvasInstance; - const createDialogs = window.document.getElementsByClassName('cvat-create-issue-dialog'); - const hiddenIssues = window.document.getElementsByClassName('cvat-hidden-issue-label'); - const issues = window.document.getElementsByClassName('cvat-issue-dialog'); - for (const element of [...Array.from(createDialogs), ...Array.from(hiddenIssues), ...Array.from(issues)]) { - (element as HTMLSpanElement).style.transform = `scale(${1 / geometry.scale}) rotate(${-geometry.angle}deg)`; - } -}; - export default function IssueAggregatorComponent(): JSX.Element | null { const dispatch = useDispatch(); const [expandedIssue, setExpandedIssue] = useState(null); const frameIssues = useSelector((state: CombinedState): any[] => state.review.frameIssues); + const issuesHidden = useSelector((state: CombinedState): boolean => state.review.issuesHidden); + const issuesResolvedHidden = useSelector((state: CombinedState): boolean => state.review.issuesResolvedHidden); const canvasInstance = useSelector((state: CombinedState) => state.annotation.canvas.instance); const canvasIsReady = useSelector((state: CombinedState): boolean => state.annotation.canvas.ready); const newIssuePosition = useSelector((state: CombinedState): number[] | null => state.review.newIssuePosition); - const issuesHidden = useSelector((state: CombinedState): any => state.review.issuesHidden); - const issuesResolvedHidden = useSelector((state: CombinedState): any => state.review.issuesResolvedHidden); const issueFetching = useSelector((state: CombinedState): number | null => state.review.fetching.issueId); + const [geometry, setGeometry] = useState(null); const issueLabels: JSX.Element[] = []; const issueDialogs: JSX.Element[] = []; - if (!(canvasInstance instanceof Canvas)) return null; - - useEffect(() => { - scaleHandler(canvasInstance); - }); - useEffect(() => { - const regions = frameIssues.reduce((acc: Record, issue: any): Record => { - acc[issue.id] = issue.position; - return acc; - }, {}); - - if (newIssuePosition) { - regions[0] = newIssuePosition; + if (canvasInstance instanceof Canvas) { + const { geometry: updatedGeometry } = canvasInstance; + setGeometry(updatedGeometry); + + const geometryListener = (): void => { + setGeometry(canvasInstance.geometry); + }; + + canvasInstance.html().addEventListener('canvas.zoom', geometryListener); + canvasInstance.html().addEventListener('canvas.fit', geometryListener); + canvasInstance.html().addEventListener('canvas.reshape', geometryListener); + + return () => { + canvasInstance.html().removeEventListener('canvas.zoom', geometryListener); + canvasInstance.html().removeEventListener('canvas.fit', geometryListener); + canvasInstance.html().addEventListener('canvas.reshape', geometryListener); + }; } - canvasInstance.setupIssueRegions(regions); - - if (newIssuePosition) { - setExpandedIssue(null); - const element = window.document.getElementById('cvat_canvas_issue_region_0'); - if (element) { - element.style.display = 'block'; - } - } - }, [newIssuePosition]); + return () => {}; + }, [canvasInstance]); useEffect(() => { - const listener = (): void => scaleHandler(canvasInstance); + if (canvasInstance instanceof Canvas) { + type IssueRegionSet = Record; + const regions = !issuesHidden ? frameIssues + .filter((_issue: any) => !issuesResolvedHidden || !_issue.resolved) + .reduce((acc: IssueRegionSet, issue: any): IssueRegionSet => { + acc[issue.id] = { + points: issue.position, + hidden: issue.resolved, + }; + return acc; + }, {}) : {}; + + if (newIssuePosition) { + // regions[0] is always empty because key is an id of an issue (<0, >0 are possible) + regions[0] = { + points: newIssuePosition, + hidden: false, + }; + } - canvasInstance.html().addEventListener('canvas.zoom', listener); - canvasInstance.html().addEventListener('canvas.fit', listener); + canvasInstance.setupIssueRegions(regions); - return () => { - canvasInstance.html().removeEventListener('canvas.zoom', listener); - canvasInstance.html().removeEventListener('canvas.fit', listener); - }; - }, []); + if (newIssuePosition) { + setExpandedIssue(null); + const element = window.document.getElementById('cvat_canvas_issue_region_0'); + if (element) { + element.style.display = 'block'; + } + } + } + }, [newIssuePosition, frameIssues, issuesResolvedHidden, issuesHidden, canvasInstance]); - if (!canvasIsReady) { + if (!(canvasInstance instanceof Canvas) || !canvasIsReady || !geometry) { return null; } - const { geometry } = canvasInstance; for (const issue of frameIssues) { if (issuesHidden) break; const issueResolved = issue.resolved; @@ -102,7 +109,7 @@ export default function IssueAggregatorComponent(): JSX.Element | null { if (issueResolved) { const element = window.document.getElementById(`cvat_canvas_issue_region_${id}`); if (element) { - element.style.display = ''; + element.style.display = 'none'; } } }; @@ -114,6 +121,8 @@ export default function IssueAggregatorComponent(): JSX.Element | null { id={issue.id} top={minY} left={minX} + angle={-geometry.angle} + scale={1 / geometry.scale} isFetching={issueFetching !== null} comments={issue.comments} resolved={issueResolved} @@ -141,6 +150,8 @@ export default function IssueAggregatorComponent(): JSX.Element | null { id={issue.id} top={minY} left={minX} + angle={-geometry.angle} + scale={1 / geometry.scale} resolved={issueResolved} message={issue.comments[issue.comments.length - 1].message} highlight={highlight} @@ -163,7 +174,14 @@ export default function IssueAggregatorComponent(): JSX.Element | null { return ( <> - {createLeft !== null && createTop !== null && } + {createLeft !== null && createTop !== null ? ( + + ) : null} {issueDialogs} {issueLabels} diff --git a/cvat-ui/src/containers/annotation-page/canvas/canvas-wrapper.tsx b/cvat-ui/src/containers/annotation-page/canvas/canvas-wrapper.tsx index 802bb4eaa11e..fbc4b9b111e5 100644 --- a/cvat-ui/src/containers/annotation-page/canvas/canvas-wrapper.tsx +++ b/cvat-ui/src/containers/annotation-page/canvas/canvas-wrapper.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2021 Intel Corporation +// Copyright (C) 2020-2022 Intel Corporation // // SPDX-License-Identifier: MIT @@ -58,7 +58,6 @@ interface StateToProps { activatedStateID: number | null; activatedAttributeID: number | null; annotations: any[]; - frameIssues: any[] | null; frameData: any; frameAngle: number; frameFetching: boolean; @@ -175,20 +174,13 @@ function mapStateToProps(state: CombinedState): StateToProps { opacity, colorBy, selectedOpacity, outlined, outlineColor, showBitmap, showProjections, }, }, - review: { frameIssues, issuesHidden, issuesResolvedHidden }, shortcuts: { keyMap }, } = state; - const issues = frameIssues.filter((issue) => ( - !issuesHidden && [Workspace.REVIEW_WORKSPACE, Workspace.STANDARD].includes(workspace) && - !(!!issue.resolvedDate && issuesResolvedHidden) - )); - return { sidebarCollapsed, canvasInstance, jobInstance, - frameIssues: issues, frameData, frameAngle: frameAngles[frame - jobInstance.startFrame], frameFetching, diff --git a/cvat-ui/src/reducers/review-reducer.ts b/cvat-ui/src/reducers/review-reducer.ts index ea3a2429d3b9..9f7df9e25d99 100644 --- a/cvat-ui/src/reducers/review-reducer.ts +++ b/cvat-ui/src/reducers/review-reducer.ts @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2021 Intel Corporation +// Copyright (C) 2020-2022 Intel Corporation // // SPDX-License-Identifier: MIT @@ -86,7 +86,8 @@ export default function (state: ReviewState = defaultState, action: any): Review } case ReviewActionTypes.FINISH_ISSUE_SUCCESS: { const { frame, issue } = action.payload; - const frameIssues = [...state.issues, issue].filter((_issue: any): boolean => _issue.frame === frame); + const issues = [...state.issues, issue]; + const frameIssues = issues.filter((_issue: any): boolean => _issue.frame === frame); return { ...state, @@ -103,6 +104,7 @@ export default function (state: ReviewState = defaultState, action: any): Review ), ).slice(-consts.LATEST_COMMENTS_SHOWN_QUICK_ISSUE), frameIssues, + issues, newIssuePosition: null, }; } diff --git a/cvat-ui/src/utils/math.ts b/cvat-ui/src/utils/math.ts index eb0b8ea884ce..6b5d111fd20b 100644 --- a/cvat-ui/src/utils/math.ts +++ b/cvat-ui/src/utils/math.ts @@ -1,4 +1,4 @@ -// Copyright (C) 2020-2021 Intel Corporation +// Copyright (C) 2020-2022 Intel Corporation // // SPDX-License-Identifier: MIT @@ -37,3 +37,11 @@ export function pointsToNumberArray(points: Point[]): number[] { return acc; }, []); } + +export function rotatePoint(x: number, y: number, angle: number, cx = 0, cy = 0): number[] { + const sin = Math.sin((angle * Math.PI) / 180); + const cos = Math.cos((angle * Math.PI) / 180); + const rotX = (x - cx) * cos - (y - cy) * sin + cx; + const rotY = (y - cy) * cos + (x - cx) * sin + cy; + return [rotX, rotY]; +}