cvat-ai / cvat

Annotate better with CVAT, the industry-leading data engine for machine learning. Used and trusted by teams at any scale, for data of any scale.
https://cvat.ai
MIT License
11.9k stars 2.9k forks source link

I want to change ZLayer to maxZLayer #6299

Closed yaeji98 closed 1 year ago

yaeji98 commented 1 year ago

My actions before raising this issue

Steps to Reproduce (for bugs)

Expected Behaviour

  1. draw a polygon shape
  2. right click & open a object-item-menu image
  3. click on a menu
  4. click on "To foreground"
  5. current zLayer : 1 , max zLayer : 1 image

Current Behaviour

but after click on "To foreground" , currentZLayer not change

image

I tried to fix it, not working.. \cvat-ui\src\containers\annotation-page\standard-workspace\objects-side-bar\object-item.tsx

private toForeground = (): void => {
        const { objectState, readonly, maxZLayer,switchZLayer } = this.props;

        if (!readonly) {
            objectState.zOrder = maxZLayer + 1;
            switchZLayer(maxZLayer);
            this.commit();
        }
    };

full code

// Copyright (C) 2021-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

import React from 'react';
import copy from 'copy-to-clipboard';
import { connect } from 'react-redux';

import {
    updateAnnotationsAsync,
    changeFrameAsync,
    changeGroupColorAsync,
    pasteShapeAsync,
    copyShape as copyShapeAction,
    activateObject as activateObjectAction,
    switchPropagateVisibility as switchPropagateVisibilityAction,
    removeObject as removeObjectAction,
    switchZLayer as switchZLayerAction
} from 'actions/annotation-actions';

import {
    ActiveControl, CombinedState, ColorBy, ShapeType,
} from 'reducers';
import ObjectStateItemComponent from 'components/annotation-page/standard-workspace/objects-side-bar/object-item';
import { getColor } from 'components/annotation-page/standard-workspace/objects-side-bar/shared';
import { shift } from 'utils/math';
import { Canvas, CanvasMode } from 'cvat-canvas-wrapper';
import { Canvas3d } from 'cvat-canvas3d-wrapper';
import { filterApplicableLabels } from 'utils/filter-applicable-labels';

interface OwnProps {
    readonly: boolean;
    clientID: number;
    objectStates: any[];
}

interface StateToProps {
    objectState: any;
    labels: any[];
    attributes: any[];
    jobInstance: any;
    frameNumber: number;
    activated: boolean;
    colorBy: ColorBy;
    ready: boolean;
    activeControl: ActiveControl;
    activatedElementID: number | null;
    minZLayer: number;
    maxZLayer: number;
    normalizedKeyMap: Record<string, string>;
    canvasInstance: Canvas | Canvas3d;
}

interface DispatchToProps {
    changeFrame(frame: number): void;
    updateState(objectState: any): void;
    activateObject: (activatedStateID: number | null, activatedElementID: number | null) => void;
    removeObject: (objectState: any) => void;
    copyShape: (objectState: any) => void;
    switchPropagateVisibility: (visible: boolean) => void;
    changeGroupColor(group: number, color: string): void;
    switchZLayer(cur: number): void;
}

function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
    const {
        annotation: {
            annotations: {
                activatedStateID,
                zLayer: { min: minZLayer, max: maxZLayer },
            },
            job: { attributes: jobAttributes, instance: jobInstance, labels },
            player: {
                frame: { number: frameNumber },
            },
            canvas: { instance: canvasInstance, ready, activeControl },
        },
        settings: {
            shapes: { colorBy },
        },
        shortcuts: { normalizedKeyMap },
    } = state;

    const { objectStates: states, clientID } = own;
    const stateIDs = states.map((_state: any): number => _state.clientID);
    const index = stateIDs.indexOf(clientID);

    return {
        objectState: states[index],
        attributes: jobAttributes[states[index].label.id],
        labels,
        ready,
        activeControl,
        colorBy,
        jobInstance,
        frameNumber,
        activated: activatedStateID === clientID,
        minZLayer,
        maxZLayer,
        normalizedKeyMap,
        canvasInstance,
    };
}

function mapDispatchToProps(dispatch: any): DispatchToProps {
    return {
        changeFrame(frame: number): void {
            dispatch(changeFrameAsync(frame));
        },
        updateState(state: any): void {
            dispatch(updateAnnotationsAsync([state]));
        },
        activateObject(activatedStateID: number | null): void {
            dispatch(activateObjectAction(activatedStateID, null, null));
        },
        removeObject(objectState: any): void {
            dispatch(removeObjectAction(objectState, false));
        },
        copyShape(objectState: any): void {
            dispatch(copyShapeAction(objectState));
            dispatch(pasteShapeAsync());
        },
        switchPropagateVisibility(visible: boolean): void {
            dispatch(switchPropagateVisibilityAction(visible));
        },
        changeGroupColor(group: number, color: string): void {
            dispatch(changeGroupColorAsync(group, color));
        },
        switchZLayer(cur: number): void {
            dispatch(switchZLayerAction(cur));
        },
    };
}

type Props = StateToProps & DispatchToProps & OwnProps;
class ObjectItemContainer extends React.PureComponent<Props> {
    private copy = (): void => {
        const { objectState, readonly, copyShape } = this.props;
        if (!readonly) {
            copyShape(objectState);
        }
    };

    private propagate = (): void => {
        const { switchPropagateVisibility, readonly } = this.props;
        if (!readonly) {
            switchPropagateVisibility(true);
        }
    };

    private edit = (): void => {
        const { objectState, readonly, canvasInstance } = this.props;

        if (!readonly && canvasInstance instanceof Canvas) {
            if (canvasInstance.mode() !== CanvasMode.IDLE) {
                canvasInstance.cancel();
            }

            canvasInstance.edit({ enabled: true, state: objectState });
        }
    }

    private remove = (): void => {
        const {
            objectState, readonly, removeObject,
        } = this.props;

        if (!readonly) {
            removeObject(objectState);
        }
    };

    private createURL = (): void => {
        const { objectState, frameNumber } = this.props;
        const { origin, pathname } = window.location;

        const search = `frame=${frameNumber}&type=${objectState.objectType}&serverID=${objectState.serverID}`;
        const url = `${origin}${pathname}?${search}`;
        copy(url);
    };

    private switchOrientation = (): void => {
        const { objectState, readonly, updateState } = this.props;
        if (readonly) {
            return;
        }

        if (objectState.shapeType === ShapeType.CUBOID) {
            this.switchCuboidOrientation();
            return;
        }

        const reducedPoints = objectState.points.reduce(
            (acc: number[][], _: number, index: number, array: number[]): number[][] => {
                if (index % 2) {
                    acc.push([array[index - 1], array[index]]);
                }

                return acc;
            },
            [],
        );

        if (objectState.shapeType === ShapeType.POLYGON) {
            objectState.points = reducedPoints.slice(0, 1).concat(reducedPoints.reverse().slice(0, -1)).flat();
            updateState(objectState);
        } else if (objectState.shapeType === ShapeType.POLYLINE) {
            objectState.points = reducedPoints.reverse().flat();
            updateState(objectState);
        }
    };

    private toBackground = (): void => {
        const { objectState, readonly, minZLayer } = this.props;

        if (!readonly) {
            objectState.zOrder = minZLayer - 1;
            this.commit();
        }
    };

    private toForeground = (): void => {
        const { objectState, readonly, maxZLayer,switchZLayer } = this.props;

        if (!readonly) {
            objectState.zOrder = maxZLayer + 1;
            switchZLayer(maxZLayer);
            this.commit();
        }
    };

    private activate = (activeElementID?: number): void => {
        const {
            objectState, ready, activeControl, activateObject,
        } = this.props;

        if (ready && activeControl === ActiveControl.CURSOR) {
            activateObject(
                objectState.clientID,
                (Number.isInteger(activeElementID) ? activeElementID : null) as number | null,
            );
        }
    };

    private changeColor = (color: string): void => {
        const { objectState, colorBy, changeGroupColor } = this.props;

        if (colorBy === ColorBy.INSTANCE) {
            objectState.color = color;
            this.commit();
        } else if (colorBy === ColorBy.GROUP) {
            changeGroupColor(objectState.group.id, color);
        }
    };

    private changeLabel = (label: any): void => {
        const { objectState, readonly } = this.props;
        if (!readonly) {
            objectState.label = label;
            this.commit();
        }
    };

    private switchCuboidOrientation = (): void => {
        function cuboidOrientationIsLeft(points: number[]): boolean {
            return points[12] > points[0];
        }

        const { objectState, readonly } = this.props;

        if (!readonly) {
            this.resetCuboidPerspective(false);
            objectState.points = shift(objectState.points, cuboidOrientationIsLeft(objectState.points) ? 4 : -4);

            this.commit();
        }
    };

    private resetCuboidPerspective = (commit = true): void => {
        function cuboidOrientationIsLeft(points: number[]): boolean {
            return points[12] > points[0];
        }
        const { objectState, readonly } = this.props;

        if (!readonly) {
            const { points } = objectState;
            const minD = {
                x: (points[6] - points[2]) * 0.001,
                y: (points[3] - points[1]) * 0.001,
            };

            if (cuboidOrientationIsLeft(points)) {
                points[14] = points[10] + points[2] - points[6] + minD.x;
                points[15] = points[11] + points[3] - points[7];
                points[8] = points[10] + points[4] - points[6];
                points[9] = points[11] + points[5] - points[7] + minD.y;
                points[12] = points[14] + points[0] - points[2];
                points[13] = points[15] + points[1] - points[3] + minD.y;
            } else {
                points[10] = points[14] + points[6] - points[2] - minD.x;
                points[11] = points[15] + points[7] - points[3];
                points[12] = points[14] + points[0] - points[2];
                points[13] = points[15] + points[1] - points[3] + minD.y;
                points[8] = points[12] + points[4] - points[0] - minD.x;
                points[9] = points[13] + points[5] - points[1];
            }

            objectState.points = points;
            if (commit) this.commit();
        }
    };

    private commit(): void {
        const { objectState, readonly, updateState } = this.props;
        if (!readonly) {
            updateState(objectState);
        }
    }

    public render(): JSX.Element {
        const {
            objectState,
            labels,
            attributes,
            activated,
            colorBy,
            normalizedKeyMap,
            readonly,
            jobInstance,
        } = this.props;

        const applicableLabels = filterApplicableLabels(objectState, labels);

        return (
            <ObjectStateItemComponent
                jobInstance={jobInstance}
                readonly={readonly}
                activated={activated}
                objectType={objectState.objectType}
                shapeType={objectState.shapeType}
                clientID={objectState.clientID}
                serverID={objectState.serverID}
                locked={objectState.lock}
                labelID={objectState.label.id}
                color={getColor(objectState, colorBy)}
                attributes={attributes}
                elements={objectState.elements}
                normalizedKeyMap={normalizedKeyMap}
                labels={applicableLabels}
                colorBy={colorBy}
                activate={this.activate}
                remove={this.remove}
                copy={this.copy}
                propagate={this.propagate}
                createURL={this.createURL}
                switchOrientation={this.switchOrientation}
                toBackground={this.toBackground}
                toForeground={this.toForeground}
                changeColor={this.changeColor}
                changeLabel={this.changeLabel}
                edit={this.edit}
                resetCuboidPerspective={() => this.resetCuboidPerspective()}
            />
        );
    }
}

export default connect<StateToProps, DispatchToProps, OwnProps, CombinedState>(
    mapStateToProps,
    mapDispatchToProps,
)(ObjectItemContainer);

Possible Solution

Context

Your Environment

bsekachev commented 1 year ago

Current z layer is not expected to be updated when moving an object to foreground. In your code:

if (!readonly) {
            objectState.zOrder = maxZLayer + 1;
            switchZLayer(maxZLayer);
            this.commit();
        }

switchZLayer(maxZLayer) do not do anything because new max layer was just set to maxZLayer + 1.

yaeji98 commented 1 year ago

Current z layer is not expected to be updated when moving an object to foreground. In your code:

if (!readonly) {
            objectState.zOrder = maxZLayer + 1;
            switchZLayer(maxZLayer);
            this.commit();
        }

switchZLayer(maxZLayer) do not do anything because new max layer was just set to maxZLayer + 1.

@bsekachev , Thank you for your answer!!!!

private toForeground = (): void => {
        const { objectState, readonly, maxZLayer,switchZLayer } = this.props;

        if (!readonly) {
            objectState.zOrder = maxZLayer + 1;
            switchZLayer(objectState.zOrder);
            this.commit();
        }   
    };

but Changing to the above code does not work. Is there another problem?

bsekachev commented 1 year ago

Dispatching switchZLayer action must be performed after updating global state with new annotations objects. Since updating the global state is async, there are two ways to do it:

  1. Quick: wrap switchZLayer into setTimeout(() => switchZLayer(objectState.zOrder))
  2. Correct: call switchZLayer in componentDidUpdate somehow.

Additionally, if you are using shortucts, they are handled by other code (not good, I know). In this file: cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/objects-list.tsx

image
yaeji98 commented 1 year ago

@bsekachev , Thank you for your answer!!!! The problem has been resolved. Thank you for your quick reply.