Batch of fixes (#1370)

* Some margins were change to paddings

* Removed extra selected

* Fix: added outside shapes when merge polyshapes

* Fixed double scroll bars

* Updated canvas table

* Fixed setup methodf

* Disabled change frame during drag, resize and editing

* Fixed: hidden points are visible

* Fixed: Merge is allowed for points, but clicks on points conflict with frame dragging logic

* Fixed: do not filter removed objects

* Updated CHANGELOG.md

* Couple of headers updated
main
Boris Sekachev 6 years ago committed by GitHub
parent 76fc8e442d
commit 1d78c54029
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Increase preview size of a task till 256, 256 on the server
- Minor style updates
### Deprecated
-
@ -23,8 +24,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- New shape is added when press ``esc`` when drawing instead of cancellation
- Fixed dextr segmentation.
- Fixed `FileNotFoundError` during dump after moving format files
- Dextr segmentation doesn't work.
- `FileNotFoundError` during dump after moving format files
- CVAT doesn't append outside shapes when merge polyshapes in old UI
- Layout sometimes shows double scroll bars on create task, dashboard and settings pages
- UI fails after trying to change frame during resizing, dragging, editing
- Hidden points (or outsided) are visible after changing a frame
- Merge is allowed for points, but clicks on points conflict with frame dragging logic
- Removed objects are visible for search
### Security
-

@ -179,23 +179,23 @@ Standard JS events are used.
## API Reaction
| | IDLE | GROUPING | SPLITTING | DRAWING | MERGING | EDITING | DRAG | ZOOM |
|--------------|------|----------|-----------|---------|---------|---------|------|------|
| html() | + | + | + | + | + | + | + | + |
| setup() | + | + | + | + | + | - | + | + |
| activate() | + | - | - | - | - | - | - | - |
| rotate() | + | + | + | + | + | + | + | + |
| focus() | + | + | + | + | + | + | + | + |
| fit() | + | + | + | + | + | + | + | + |
| grid() | + | + | + | + | + | + | + | + |
| draw() | + | - | - | - | - | - | - | - |
| split() | + | - | + | - | - | - | - | - |
| group() | + | + | - | - | - | - | - | - |
| merge() | + | - | - | - | + | - | - | - |
| fitCanvas() | + | + | + | + | + | + | + | + |
| dragCanvas() | + | - | - | - | - | - | + | - |
| zoomCanvas() | + | - | - | - | - | - | - | + |
| cancel() | - | + | + | + | + | + | + | + |
| configure() | + | - | - | - | - | - | - | - |
| bitmap() | + | + | + | + | + | + | + | + |
| setZLayer() | + | + | + | + | + | + | + | + |
| | IDLE | GROUP | SPLIT | DRAW | MERGE | EDIT | DRAG | RESIZE | ZOOM_CANVAS | DRAG_CANVAS |
|--------------|------|-------|-------|------|-------|------|------|--------|-------------|-------------|
| html() | + | + | + | + | + | + | + | + | + | + |
| setup() | + | + | + | + | + | - | - | - | + | + |
| activate() | + | - | - | - | - | - | - | - | - | - |
| rotate() | + | + | + | + | + | + | + | + | + | + |
| focus() | + | + | + | + | + | + | + | + | + | + |
| fit() | + | + | + | + | + | + | + | + | + | + |
| grid() | + | + | + | + | + | + | + | + | + | + |
| draw() | + | - | - | - | - | - | - | - | - | - |
| split() | + | - | + | - | - | - | - | - | - | - |
| group() | + | + | - | - | - | - | - | - | - | - |
| merge() | + | - | - | - | + | - | - | - | - | - |
| fitCanvas() | + | + | + | + | + | + | + | + | + | + |
| dragCanvas() | + | - | - | - | - | - | + | - | - | + |
| zoomCanvas() | + | - | - | - | - | - | - | + | + | - |
| cancel() | - | + | + | + | + | + | + | + | + | + |
| configure() | + | - | - | - | - | - | - | - | - | - |
| bitmap() | + | + | + | + | + | + | + | + | + | + |
| setZLayer() | + | + | + | + | + | + | + | + | + | + |

@ -327,6 +327,10 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
}
public setup(frameData: any, objectStates: any[]): void {
if ([Mode.EDIT, Mode.DRAG, Mode.RESIZE].includes(this.data.mode)) {
throw Error(`Canvas is busy. Action: ${this.data.mode}`);
}
if (frameData.number === this.data.imageID) {
this.data.objects = objectStates;
this.notify(UpdateReasons.OBJECTS_UPDATED);

@ -1041,7 +1041,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
(shape as any).clear();
shape.attr('points', stringified);
if (state.shapeType === 'points') {
if (state.shapeType === 'points' && !state.hidden) {
this.selectize(false, shape);
this.setupPoints(shape as SVG.PolyLine, state);
}
@ -1187,7 +1187,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
(shape as any).off('resizestart');
(shape as any).off('resizing');
(shape as any).off('resizedone');
(shape as any).resize(false);
(shape as any).resize('stop');
// TODO: Hide text only if it is hidden by settings
const text = this.svgTexts[clientID];
@ -1543,6 +1543,8 @@ export class CanvasViewImpl implements CanvasView, Listener {
group.on('click.canvas', (event: MouseEvent): void => {
// Need to redispatch the event on another element
basicPolyline.fire(new MouseEvent('click', event));
// redispatch event to canvas to be able merge points clicking them
this.content.dispatchEvent(new MouseEvent('click', event));
});
group.bbox = basicPolyline.bbox.bind(basicPolyline);

@ -871,8 +871,10 @@
// In particular consider first and last frame as keyframes for all frames
const statesData = [].concat(
(frame in this.shapes ? this.shapes[frame] : [])
.filter((shape) => !shape.removed)
.map((shape) => shape.get(frame)),
(frame in this.tags ? this.tags[frame] : [])
.filter((tag) => !tag.removed)
.map((tag) => tag.get(frame)),
);
const tracks = Object.values(this.tracks)
@ -880,7 +882,7 @@
frame in track.shapes
|| frame === frameFrom
|| frame === frameTo
));
)).filter((track) => !track.removed);
statesData.push(
...tracks.map((track) => track.get(frame))
.filter((state) => !state.outside),

@ -36,7 +36,17 @@ export default function AnnotationPageComponent(props: Props): JSX.Element {
useEffect(() => {
saveLogs();
return saveLogs;
const root = window.document.getElementById('root');
if (root) {
root.style.minHeight = '768px';
}
return () => {
saveLogs();
if (root) {
root.style.minHeight = '';
}
};
}, []);
if (job === null) {

@ -6,7 +6,7 @@
.cvat-create-task-form-wrapper {
text-align: center;
margin-top: 40px;
padding-top: 40px;
overflow-y: auto;
height: 90%;

@ -9,7 +9,7 @@
height: 100%;
> div:nth-child(1) {
margin-bottom: 10px;
padding-bottom: 10px;
div > {
span {
@ -36,11 +36,11 @@
> div:nth-child(3) {
height: 83%;
margin-top: 10px;
padding-top: 10px;
}
> div:nth-child(4) {
margin-top: 10px;
padding-top: 10px;
}
}

@ -7,6 +7,7 @@ import copy from 'copy-to-clipboard';
import { connect } from 'react-redux';
import { LogType } from 'cvat-logger';
import { Canvas, isAbleToChangeFrame } from 'cvat-canvas';
import { ActiveControl, CombinedState, ColorBy } from 'reducers/interfaces';
import {
collapseObjectItems,
@ -24,7 +25,6 @@ import {
import ObjectStateItemComponent from 'components/annotation-page/standard-workspace/objects-side-bar/object-item';
interface OwnProps {
clientID: number;
}
@ -44,6 +44,7 @@ interface StateToProps {
minZLayer: number;
maxZLayer: number;
normalizedKeyMap: Record<string, string>;
canvasInstance: Canvas;
}
interface DispatchToProps {
@ -84,6 +85,7 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
canvas: {
ready,
activeControl,
instance: canvasInstance,
},
colors,
},
@ -119,6 +121,7 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
minZLayer,
maxZLayer,
normalizedKeyMap,
canvasInstance,
};
}
@ -166,72 +169,44 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
type Props = StateToProps & DispatchToProps;
class ObjectItemContainer extends React.PureComponent<Props> {
private navigateFirstKeyframe = (): void => {
const {
objectState,
changeFrame,
frameNumber,
} = this.props;
const { objectState, frameNumber } = this.props;
const { first } = objectState.keyframes;
if (first !== frameNumber) {
changeFrame(first);
this.changeFrame(first);
}
};
private navigatePrevKeyframe = (): void => {
const {
objectState,
changeFrame,
frameNumber,
} = this.props;
const { objectState, frameNumber } = this.props;
const { prev } = objectState.keyframes;
if (prev !== null && prev !== frameNumber) {
changeFrame(prev);
this.changeFrame(prev);
}
};
private navigateNextKeyframe = (): void => {
const {
objectState,
changeFrame,
frameNumber,
} = this.props;
const { objectState, frameNumber } = this.props;
const { next } = objectState.keyframes;
if (next !== null && next !== frameNumber) {
changeFrame(next);
this.changeFrame(next);
}
};
private navigateLastKeyframe = (): void => {
const {
objectState,
changeFrame,
frameNumber,
} = this.props;
const { objectState, frameNumber } = this.props;
const { last } = objectState.keyframes;
if (last !== frameNumber) {
changeFrame(last);
this.changeFrame(last);
}
};
private copy = (): void => {
const {
objectState,
copyShape,
} = this.props;
const { objectState, copyShape } = this.props;
copyShape(objectState);
};
private propagate = (): void => {
const {
objectState,
propagateObject,
} = this.props;
const { objectState, propagateObject } = this.props;
propagateObject(objectState);
};
@ -422,6 +397,13 @@ class ObjectItemContainer extends React.PureComponent<Props> {
this.commit();
};
private changeFrame(frame: number): void {
const { changeFrame, canvasInstance } = this.props;
if (isAbleToChangeFrame(canvasInstance)) {
changeFrame(frame);
}
}
private commit(): void {
const {
objectState,

@ -15,6 +15,7 @@ import {
copyShape as copyShapeAction,
propagateObject as propagateObjectAction,
} from 'actions/annotation-actions';
import { Canvas, isAbleToChangeFrame } from 'cvat-canvas';
import { CombinedState, StatesOrdering, ObjectType } from 'reducers/interfaces';
interface StateToProps {
@ -32,6 +33,7 @@ interface StateToProps {
annotationsFiltersHistory: string[];
keyMap: Record<string, ExtendedKeyMapOptions>;
normalizedKeyMap: Record<string, string>;
canvasInstance: Canvas;
}
interface DispatchToProps {
@ -65,6 +67,9 @@ function mapStateToProps(state: CombinedState): StateToProps {
number: frameNumber,
},
},
canvas: {
instance: canvasInstance,
},
tabContentHeight: listHeight,
},
shortcuts: {
@ -104,6 +109,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
annotationsFiltersHistory,
keyMap,
normalizedKeyMap,
canvasInstance,
};
}
@ -254,6 +260,7 @@ class ObjectsListContainer extends React.PureComponent<Props, State> {
minZLayer,
keyMap,
normalizedKeyMap,
canvasInstance,
} = this.props;
const {
sortedStatesID,
@ -388,7 +395,7 @@ class ObjectsListContainer extends React.PureComponent<Props, State> {
if (state && state.objectType === ObjectType.TRACK) {
const frame = typeof (state.keyframes.next) === 'number'
? state.keyframes.next : null;
if (frame !== null) {
if (frame !== null && isAbleToChangeFrame(canvasInstance)) {
changeFrame(frame);
}
}
@ -399,7 +406,7 @@ class ObjectsListContainer extends React.PureComponent<Props, State> {
if (state && state.objectType === ObjectType.TRACK) {
const frame = typeof (state.keyframes.prev) === 'number'
? state.keyframes.prev : null;
if (frame !== null) {
if (frame !== null && isAbleToChangeFrame(canvasInstance)) {
changeFrame(frame);
}
}

@ -23,6 +23,7 @@ import {
changeWorkspace as changeWorkspaceAction,
activateObject,
} from 'actions/annotation-actions';
import { Canvas, isAbleToChangeFrame } from 'cvat-canvas';
import AnnotationTopBarComponent from 'components/annotation-page/top-bar/top-bar';
import { CombinedState, FrameSpeed, Workspace } from 'reducers/interfaces';
@ -45,6 +46,7 @@ interface StateToProps {
workspace: Workspace;
keyMap: Record<string, ExtendedKeyMapOptions>;
normalizedKeyMap: Record<string, string>;
canvasInstance: Canvas;
}
interface DispatchToProps {
@ -81,6 +83,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
},
canvas: {
ready: canvasIsReady,
instance: canvasInstance,
},
workspace,
},
@ -118,6 +121,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
workspace,
keyMap,
normalizedKeyMap,
canvasInstance,
};
}
@ -197,6 +201,7 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
frameDelay,
playing,
canvasIsReady,
canvasInstance,
onSwitchPlay,
onChangeFrame,
} = this.props;
@ -217,10 +222,14 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
setTimeout(() => {
const { playing: stillPlaying } = this.props;
if (stillPlaying) {
onChangeFrame(
frameNumber + 1 + framesSkiped,
stillPlaying, framesSkiped + 1,
);
if (isAbleToChangeFrame(canvasInstance)) {
onChangeFrame(
frameNumber + 1 + framesSkiped,
stillPlaying, framesSkiped + 1,
);
} else {
onSwitchPlay(false);
}
}
}, frameDelay);
} else {
@ -240,9 +249,12 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
undo,
jobInstance,
frameNumber,
canvasInstance,
} = this.props;
undo(jobInstance, frameNumber);
if (isAbleToChangeFrame(canvasInstance)) {
undo(jobInstance, frameNumber);
}
};
private redo = (): void => {
@ -250,9 +262,12 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
redo,
jobInstance,
frameNumber,
canvasInstance,
} = this.props;
redo(jobInstance, frameNumber);
if (isAbleToChangeFrame(canvasInstance)) {
redo(jobInstance, frameNumber);
}
};
private showStatistics = (): void => {
@ -285,7 +300,6 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
jobInstance,
playing,
onSwitchPlay,
onChangeFrame,
} = this.props;
const newFrame = jobInstance.startFrame;
@ -293,7 +307,7 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
if (playing) {
onSwitchPlay(false);
}
onChangeFrame(newFrame);
this.changeFrame(newFrame);
}
};
@ -304,7 +318,6 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
jobInstance,
playing,
onSwitchPlay,
onChangeFrame,
} = this.props;
const newFrame = Math
@ -313,7 +326,7 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
if (playing) {
onSwitchPlay(false);
}
onChangeFrame(newFrame);
this.changeFrame(newFrame);
}
};
@ -323,7 +336,6 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
jobInstance,
playing,
onSwitchPlay,
onChangeFrame,
} = this.props;
const newFrame = Math
@ -332,7 +344,7 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
if (playing) {
onSwitchPlay(false);
}
onChangeFrame(newFrame);
this.changeFrame(newFrame);
}
};
@ -342,7 +354,6 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
jobInstance,
playing,
onSwitchPlay,
onChangeFrame,
} = this.props;
const newFrame = Math
@ -351,7 +362,7 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
if (playing) {
onSwitchPlay(false);
}
onChangeFrame(newFrame);
this.changeFrame(newFrame);
}
};
@ -362,7 +373,6 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
jobInstance,
playing,
onSwitchPlay,
onChangeFrame,
} = this.props;
const newFrame = Math
@ -371,7 +381,7 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
if (playing) {
onSwitchPlay(false);
}
onChangeFrame(newFrame);
this.changeFrame(newFrame);
}
};
@ -381,7 +391,6 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
jobInstance,
playing,
onSwitchPlay,
onChangeFrame,
} = this.props;
const newFrame = jobInstance.stopFrame;
@ -389,7 +398,7 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
if (playing) {
onSwitchPlay(false);
}
onChangeFrame(newFrame);
this.changeFrame(newFrame);
}
};
@ -403,22 +412,16 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
};
private onChangePlayerSliderValue = (value: SliderValue): void => {
const {
playing,
onSwitchPlay,
onChangeFrame,
} = this.props;
const { playing, onSwitchPlay } = this.props;
if (playing) {
onSwitchPlay(false);
}
onChangeFrame(value as number);
this.changeFrame(value as number);
};
private onChangePlayerInputValue = (value: number): void => {
const {
onSwitchPlay,
onChangeFrame,
playing,
frameNumber,
} = this.props;
@ -427,7 +430,7 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
if (playing) {
onSwitchPlay(false);
}
onChangeFrame(value);
this.changeFrame(value);
}
};
@ -441,6 +444,13 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
copy(url);
};
private changeFrame(frame: number): void {
const { onChangeFrame, canvasInstance } = this.props;
if (isAbleToChangeFrame(canvasInstance)) {
onChangeFrame(frame);
}
}
private beforeUnloadCallback(event: BeforeUnloadEvent): any {
const { jobInstance } = this.props;
if (jobInstance.annotations.hasUnsavedChanges()) {
@ -472,6 +482,7 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
changeWorkspace,
keyMap,
normalizedKeyMap,
canvasInstance,
} = this.props;
const preventDefault = (event: KeyboardEvent | undefined): void => {
@ -537,13 +548,17 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
},
SEARCH_FORWARD: (event: KeyboardEvent | undefined) => {
preventDefault(event);
if (frameNumber + 1 <= stopFrame && canvasIsReady) {
if (frameNumber + 1 <= stopFrame && canvasIsReady
&& isAbleToChangeFrame(canvasInstance)
) {
searchAnnotations(jobInstance, frameNumber + 1, stopFrame);
}
},
SEARCH_BACKWARD: (event: KeyboardEvent | undefined) => {
preventDefault(event);
if (frameNumber - 1 >= startFrame && canvasIsReady) {
if (frameNumber - 1 >= startFrame && canvasIsReady
&& isAbleToChangeFrame(canvasInstance)
) {
searchAnnotations(jobInstance, frameNumber - 1, startFrame);
}
},

@ -9,9 +9,15 @@ import {
RectDrawingMethod,
} from '../../cvat-canvas/src/typescript/canvas';
function isAbleToChangeFrame(canvas: Canvas): boolean {
return ![CanvasMode.DRAG, CanvasMode.EDIT, CanvasMode.RESIZE]
.includes(canvas.mode());
}
export {
Canvas,
CanvasMode,
CanvasVersion,
RectDrawingMethod,
isAbleToChangeFrame,
};

@ -48,5 +48,4 @@ hr {
height: 100%;
display: grid;
min-width: 1280px;
min-height: 768px;
}

@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 Intel Corporation
* Copyright (C) 2018-2020 Intel Corporation
*
* SPDX-License-Identifier: MIT
*/
@ -145,7 +145,7 @@ class ShapeMergerModel extends Listener {
let nextFrame = frame + 1;
let stopFrame = window.cvat.player.frames.stop;
let type = shapeDict[frame].shape.type;
if (type === 'annotation_box' && !(nextFrame in shapeDict) && nextFrame <= stopFrame) {
if (type.startsWith('annotation_') && !(nextFrame in shapeDict) && nextFrame <= stopFrame) {
let copy = Object.assign({}, object.shapes[object.shapes.length - 1]);
copy.outside = true;
copy.frame += 1;

@ -1,5 +1,5 @@
<!--
Copyright (C) 2018-2019 Intel Corporation
Copyright (C) 2018-2020 Intel Corporation
SPDX-License-Identifier: MIT
-->
@ -451,7 +451,7 @@
</select>
<select id="shapeTypeSelector" class="regular h2">
<option value="box" class="regular" selected> Box </option>
<option value="box_by_4_points" class="regular" selected> Box by 4 points </option>
<option value="box_by_4_points" class="regular"> Box by 4 points </option>
<option value="polygon" class="regular"> Polygon </option>
<option value="polyline" class="regular"> Polyline </option>
<option value="points" class="regular"> Points </option>

Loading…
Cancel
Save