React UI: Player in annotation view & settings page (#1018)
* Active player controls * Setup packages * Playing * Fold/unfold sidebar, minor issues * Improved cvat-canvas integration * Resolved some issues * Added cvat-canvas to Dockerfile.ui * Fit canvas method * Added annotation reducer * Added annotation actions * Added containers * Added components * cvat-canvas removed from dockerignore * Added settings page * Minor improvements * Container for canvas wrapper * Configurable grid * Rotation * fitCanvas added to readme * Aligned tablemain
parent
67239b6148
commit
0277547dc3
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,9 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
module.exports = {
|
||||||
|
parser: false,
|
||||||
|
plugins: {
|
||||||
|
'postcss-preset-env': {
|
||||||
|
browsers: '> 2.5%', // https://github.com/browserslist/browserslist
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1,132 @@
|
|||||||
|
import { AnyAction, Dispatch, ActionCreator } from 'redux';
|
||||||
|
import { ThunkAction } from 'redux-thunk';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CombinedState,
|
||||||
|
Task,
|
||||||
|
} from '../reducers/interfaces';
|
||||||
|
|
||||||
|
import getCore from '../core';
|
||||||
|
import { getCVATStore } from '../store';
|
||||||
|
|
||||||
|
const cvat = getCore();
|
||||||
|
|
||||||
|
export enum AnnotationActionTypes {
|
||||||
|
GET_JOB = 'GET_JOB',
|
||||||
|
GET_JOB_SUCCESS = 'GET_JOB_SUCCESS',
|
||||||
|
GET_JOB_FAILED = 'GET_JOB_FAILED',
|
||||||
|
CHANGE_FRAME = 'CHANGE_FRAME',
|
||||||
|
CHANGE_FRAME_SUCCESS = 'CHANGE_FRAME_SUCCESS',
|
||||||
|
CHANGE_FRAME_FAILED = 'CHANGE_FRAME_FAILED',
|
||||||
|
SWITCH_PLAY = 'SWITCH_PLAY',
|
||||||
|
CONFIRM_CANVAS_READY = 'CONFIRM_CANVAS_READY',
|
||||||
|
}
|
||||||
|
|
||||||
|
export function switchPlay(playing: boolean): AnyAction {
|
||||||
|
return {
|
||||||
|
type: AnnotationActionTypes.SWITCH_PLAY,
|
||||||
|
payload: {
|
||||||
|
playing,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function changeFrameAsync(toFrame: number, playing: boolean):
|
||||||
|
ThunkAction<Promise<void>, {}, {}, AnyAction> {
|
||||||
|
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
|
||||||
|
const store = getCVATStore();
|
||||||
|
const state: CombinedState = store.getState();
|
||||||
|
const { jobInstance } = state.annotation;
|
||||||
|
const currentFrame = state.annotation.frame;
|
||||||
|
|
||||||
|
const frame = Math.max(
|
||||||
|
Math.min(toFrame, jobInstance.stopFrame),
|
||||||
|
jobInstance.startFrame,
|
||||||
|
);
|
||||||
|
|
||||||
|
// !playing || state.annotation.playing prevents changing frame on the latest setTimeout
|
||||||
|
// after playing had become false
|
||||||
|
if (frame !== currentFrame && (!playing || state.annotation.playing)) {
|
||||||
|
dispatch({
|
||||||
|
type: AnnotationActionTypes.CHANGE_FRAME,
|
||||||
|
payload: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const frameData = await jobInstance.frames.get(frame);
|
||||||
|
const annotations = await jobInstance.annotations.get(frame);
|
||||||
|
dispatch({
|
||||||
|
type: AnnotationActionTypes.CHANGE_FRAME_SUCCESS,
|
||||||
|
payload: {
|
||||||
|
frame,
|
||||||
|
frameData,
|
||||||
|
annotations,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
dispatch({
|
||||||
|
type: AnnotationActionTypes.CHANGE_FRAME_FAILED,
|
||||||
|
payload: {
|
||||||
|
frame,
|
||||||
|
error,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function confirmCanvasReady(): AnyAction {
|
||||||
|
return {
|
||||||
|
type: AnnotationActionTypes.CONFIRM_CANVAS_READY,
|
||||||
|
payload: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getJobAsync(tid: number, jid: number):
|
||||||
|
ThunkAction<Promise<void>, {}, {}, AnyAction> {
|
||||||
|
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
|
||||||
|
dispatch({
|
||||||
|
type: AnnotationActionTypes.GET_JOB,
|
||||||
|
payload: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const store = getCVATStore();
|
||||||
|
const state: CombinedState = store.getState();
|
||||||
|
let task = state.tasks.current
|
||||||
|
.filter((_task: Task) => _task.instance.id === tid)
|
||||||
|
.map((_task: Task) => _task.instance)[0];
|
||||||
|
if (!task) {
|
||||||
|
[task] = await cvat.tasks.get({ id: tid });
|
||||||
|
}
|
||||||
|
|
||||||
|
const job = task.jobs
|
||||||
|
.filter((_job: any) => _job.id === jid)[0];
|
||||||
|
if (!job) {
|
||||||
|
throw new Error('Job with specified id does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
const frame = Math.min(0, job.startFrame);
|
||||||
|
const frameData = await job.frames.get(frame);
|
||||||
|
const annotations = await job.annotations.get(frame);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: AnnotationActionTypes.GET_JOB_SUCCESS,
|
||||||
|
payload: {
|
||||||
|
jobInstance: job,
|
||||||
|
frameData,
|
||||||
|
annotations,
|
||||||
|
frame,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
dispatch({
|
||||||
|
type: AnnotationActionTypes.GET_JOB_FAILED,
|
||||||
|
payload: {
|
||||||
|
error,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
import { AnyAction } from 'redux';
|
||||||
|
import {
|
||||||
|
GridColor,
|
||||||
|
} from '../reducers/interfaces';
|
||||||
|
|
||||||
|
export enum SettingsActionTypes {
|
||||||
|
SWITCH_ROTATE_ALL = 'SWITCH_ROTATE_ALL',
|
||||||
|
SWITCH_GRID = 'SWITCH_GRID',
|
||||||
|
CHANGE_GRID_SIZE = 'CHANGE_GRID_SIZE',
|
||||||
|
CHANGE_GRID_COLOR = 'CHANGE_GRID_COLOR',
|
||||||
|
CHANGE_GRID_OPACITY = 'CHANGE_GRID_OPACITY',
|
||||||
|
}
|
||||||
|
|
||||||
|
export function switchRotateAll(rotateAll: boolean): AnyAction {
|
||||||
|
return {
|
||||||
|
type: SettingsActionTypes.SWITCH_ROTATE_ALL,
|
||||||
|
payload: {
|
||||||
|
rotateAll,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function switchGrid(grid: boolean): AnyAction {
|
||||||
|
return {
|
||||||
|
type: SettingsActionTypes.SWITCH_GRID,
|
||||||
|
payload: {
|
||||||
|
grid,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function changeGridSize(gridSize: number): AnyAction {
|
||||||
|
return {
|
||||||
|
type: SettingsActionTypes.CHANGE_GRID_SIZE,
|
||||||
|
payload: {
|
||||||
|
gridSize,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function changeGridColor(gridColor: GridColor): AnyAction {
|
||||||
|
return {
|
||||||
|
type: SettingsActionTypes.CHANGE_GRID_COLOR,
|
||||||
|
payload: {
|
||||||
|
gridColor,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function changeGridOpacity(gridOpacity: number): AnyAction {
|
||||||
|
return {
|
||||||
|
type: SettingsActionTypes.CHANGE_GRID_OPACITY,
|
||||||
|
payload: {
|
||||||
|
gridOpacity,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
<!-- Drawn in https://www.iconfinder.com/editor/ -->
|
||||||
|
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g transform="scale(0.07812)" transform-origin="bottom">
|
||||||
|
<path d="m201.350937,473.371796l0,-434.86451c0,-8.10006 -6.528412,-14.628468 -14.749344,-14.628468l-86.561874,0c-8.220955,0 -14.749359,6.528408 -14.749359,14.628468l0,434.86454c0,8.100037 6.528404,14.749359 14.749359,14.749359l86.561874,0c8.220932,0 14.749344,-6.528412 14.749344,-14.74939z" />
|
||||||
|
<path d="m423.967224,473.371796l0,-434.86451c0,-8.10006 -6.528381,-14.628468 -14.749329,-14.628468l-86.56189,0c-8.220947,0 -14.749329,6.528408 -14.749329,14.628468l0,434.86454c0,8.100037 6.528381,14.749359 14.749329,14.749359l86.56189,0c8.220947,0 14.749329,-6.528412 14.749329,-14.74939z" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 798 B |
@ -0,0 +1,9 @@
|
|||||||
|
import {
|
||||||
|
Canvas,
|
||||||
|
Rotation,
|
||||||
|
} from '../../cvat-canvas/src/typescript/canvas';
|
||||||
|
|
||||||
|
export {
|
||||||
|
Canvas,
|
||||||
|
Rotation,
|
||||||
|
};
|
||||||
@ -1,15 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Layout,
|
|
||||||
} from 'antd';
|
|
||||||
|
|
||||||
export default function CanvasWrapperComponent(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<Layout.Content
|
|
||||||
className='cvat-annotation-page-canvas-container'
|
|
||||||
>
|
|
||||||
main content
|
|
||||||
</Layout.Content>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -0,0 +1,139 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Layout,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import {
|
||||||
|
GridColor,
|
||||||
|
} from '../../../reducers/interfaces';
|
||||||
|
|
||||||
|
import { Canvas } from '../../../canvas';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
jobInstance: any;
|
||||||
|
annotations: any[];
|
||||||
|
frameData: any;
|
||||||
|
grid: boolean;
|
||||||
|
gridSize: number;
|
||||||
|
gridColor: GridColor;
|
||||||
|
gridOpacity: number;
|
||||||
|
onSetupCanvas: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class CanvasWrapperComponent extends React.PureComponent<Props> {
|
||||||
|
public componentDidMount(): void {
|
||||||
|
const {
|
||||||
|
canvasInstance,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
// It's awful approach from the point of view React
|
||||||
|
// But we do not have another way because cvat-canvas returns regular DOM element
|
||||||
|
const [wrapper] = window.document
|
||||||
|
.getElementsByClassName('cvat-annotation-page-canvas-container');
|
||||||
|
wrapper.appendChild(canvasInstance.html());
|
||||||
|
|
||||||
|
this.initialSetup();
|
||||||
|
this.updateCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidUpdate(prevProps: Props): void {
|
||||||
|
const {
|
||||||
|
grid,
|
||||||
|
gridSize,
|
||||||
|
gridColor,
|
||||||
|
gridOpacity,
|
||||||
|
canvasInstance,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (prevProps.grid !== grid) {
|
||||||
|
const gridElement = window.document.getElementById('cvat_canvas_grid');
|
||||||
|
if (gridElement) {
|
||||||
|
gridElement.style.display = grid ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevProps.gridSize !== gridSize) {
|
||||||
|
canvasInstance.grid(gridSize, gridSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevProps.gridColor !== gridColor) {
|
||||||
|
const gridPattern = window.document.getElementById('cvat_canvas_grid_pattern');
|
||||||
|
if (gridPattern) {
|
||||||
|
gridPattern.style.stroke = gridColor.toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevProps.gridOpacity !== gridOpacity) {
|
||||||
|
const gridPattern = window.document.getElementById('cvat_canvas_grid_pattern');
|
||||||
|
if (gridPattern) {
|
||||||
|
gridPattern.style.opacity = `${gridOpacity / 100}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
|
private initialSetup(): void {
|
||||||
|
const {
|
||||||
|
grid,
|
||||||
|
gridSize,
|
||||||
|
gridColor,
|
||||||
|
gridOpacity,
|
||||||
|
canvasInstance,
|
||||||
|
jobInstance,
|
||||||
|
onSetupCanvas,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
// Size
|
||||||
|
canvasInstance.fitCanvas();
|
||||||
|
|
||||||
|
// Grid
|
||||||
|
const gridElement = window.document.getElementById('cvat_canvas_grid');
|
||||||
|
const gridPattern = window.document.getElementById('cvat_canvas_grid_pattern');
|
||||||
|
if (gridElement) {
|
||||||
|
gridElement.style.display = grid ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
if (gridPattern) {
|
||||||
|
gridPattern.style.stroke = gridColor.toLowerCase();
|
||||||
|
gridPattern.style.opacity = `${gridOpacity / 100}`;
|
||||||
|
}
|
||||||
|
canvasInstance.grid(gridSize, gridSize);
|
||||||
|
|
||||||
|
// Events
|
||||||
|
canvasInstance.html().addEventListener('canvas.setup', (): void => {
|
||||||
|
onSetupCanvas();
|
||||||
|
if (jobInstance.task.mode === 'annotation') {
|
||||||
|
canvasInstance.fit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
canvasInstance.html().addEventListener('canvas.setup', () => {
|
||||||
|
canvasInstance.fit();
|
||||||
|
}, { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateCanvas(): void {
|
||||||
|
const {
|
||||||
|
annotations,
|
||||||
|
frameData,
|
||||||
|
canvasInstance,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (frameData !== null) {
|
||||||
|
canvasInstance.setup(frameData, annotations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): JSX.Element {
|
||||||
|
return (
|
||||||
|
// This element doesn't have any props
|
||||||
|
// So, React isn't going to rerender it
|
||||||
|
// And it's a reason why cvat-canvas appended in mount function works
|
||||||
|
<Layout.Content
|
||||||
|
className='cvat-annotation-page-canvas-container'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,217 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Row,
|
||||||
|
Col,
|
||||||
|
Checkbox,
|
||||||
|
Slider,
|
||||||
|
Select,
|
||||||
|
InputNumber,
|
||||||
|
Icon,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import Text from 'antd/lib/typography/Text';
|
||||||
|
import { CheckboxChangeEvent } from 'antd/lib/checkbox';
|
||||||
|
|
||||||
|
import {
|
||||||
|
PlaycontrolBackJumpIcon,
|
||||||
|
PlaycontrolForwardJumpIcon,
|
||||||
|
} from '../../icons';
|
||||||
|
|
||||||
|
import {
|
||||||
|
FrameSpeed,
|
||||||
|
GridColor,
|
||||||
|
} from '../../reducers/interfaces';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
frameStep: number;
|
||||||
|
frameSpeed: FrameSpeed;
|
||||||
|
resetZoom: boolean;
|
||||||
|
rotateAll: boolean;
|
||||||
|
grid: boolean;
|
||||||
|
gridSize: number;
|
||||||
|
gridColor: GridColor;
|
||||||
|
gridOpacity: number;
|
||||||
|
brightnessLevel: number;
|
||||||
|
contrastLevel: number;
|
||||||
|
saturationLevel: number;
|
||||||
|
onChangeFrameStep(step: number): void;
|
||||||
|
onChangeFrameSpeed(speed: FrameSpeed): void;
|
||||||
|
onSwitchResetZoom(enabled: boolean): void;
|
||||||
|
onSwitchRotateAll(rotateAll: boolean): void;
|
||||||
|
onSwitchGrid(grid: boolean): void;
|
||||||
|
onChangeGridSize(gridSize: number): void;
|
||||||
|
onChangeGridColor(gridColor: GridColor): void;
|
||||||
|
onChangeGridOpacity(gridOpacity: number): void;
|
||||||
|
onChangeBrightnessLevel(level: number): void;
|
||||||
|
onChangeContrastLevel(level: number): void;
|
||||||
|
onChangeSaturationLevel(level: number): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PlayerSettingsComponent(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
frameStep,
|
||||||
|
frameSpeed,
|
||||||
|
resetZoom,
|
||||||
|
rotateAll,
|
||||||
|
grid,
|
||||||
|
gridSize,
|
||||||
|
gridColor,
|
||||||
|
gridOpacity,
|
||||||
|
brightnessLevel,
|
||||||
|
contrastLevel,
|
||||||
|
saturationLevel,
|
||||||
|
onSwitchRotateAll,
|
||||||
|
onSwitchGrid,
|
||||||
|
onChangeGridSize,
|
||||||
|
onChangeGridColor,
|
||||||
|
onChangeGridOpacity,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='cvat-player-settings'>
|
||||||
|
<Row type='flex' align='bottom' className='cvat-player-settings-step'>
|
||||||
|
<Col>
|
||||||
|
<Text className='cvat-text-color'> Player step </Text>
|
||||||
|
<InputNumber min={2} max={1000} value={frameStep} />
|
||||||
|
</Col>
|
||||||
|
<Col offset={1}>
|
||||||
|
<Text type='secondary'>
|
||||||
|
Number of frames skipped when selecting
|
||||||
|
<Icon component={PlaycontrolBackJumpIcon} />
|
||||||
|
or
|
||||||
|
<Icon component={PlaycontrolForwardJumpIcon} />
|
||||||
|
</Text>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row type='flex' align='middle' className='cvat-player-settings-speed'>
|
||||||
|
<Col>
|
||||||
|
<Text className='cvat-text-color'> Player speed </Text>
|
||||||
|
<Select value={frameSpeed}>
|
||||||
|
<Select.Option key='fastest' value={FrameSpeed.Fastest}>Fastest</Select.Option>
|
||||||
|
<Select.Option key='fast' value={FrameSpeed.Fast}>Fast</Select.Option>
|
||||||
|
<Select.Option key='usual' value={FrameSpeed.Usual}>Usual</Select.Option>
|
||||||
|
<Select.Option key='slow' value={FrameSpeed.Slow}>Slow</Select.Option>
|
||||||
|
<Select.Option key='slower' value={FrameSpeed.Slower}>Slower</Select.Option>
|
||||||
|
<Select.Option key='slowest' value={FrameSpeed.Slowest}>Slowest</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row type='flex'>
|
||||||
|
<Col>
|
||||||
|
<Checkbox
|
||||||
|
className='cvat-text-color cvat-player-settings-grid'
|
||||||
|
checked={grid}
|
||||||
|
onChange={(event: CheckboxChangeEvent): void => {
|
||||||
|
onSwitchGrid(event.target.checked);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Show grid
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row type='flex' justify='space-between'>
|
||||||
|
<Col span={8} className='cvat-player-settings-grid-size'>
|
||||||
|
<Text className='cvat-text-color'> Grid size </Text>
|
||||||
|
<InputNumber
|
||||||
|
min={5}
|
||||||
|
max={1000}
|
||||||
|
step={1}
|
||||||
|
value={gridSize}
|
||||||
|
onChange={(value: number | undefined): void => {
|
||||||
|
if (value) {
|
||||||
|
onChangeGridSize(value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={8} className='cvat-player-settings-grid-color'>
|
||||||
|
<Text className='cvat-text-color'> Grid color </Text>
|
||||||
|
<Select
|
||||||
|
value={gridColor}
|
||||||
|
onChange={(color: GridColor): void => {
|
||||||
|
onChangeGridColor(color);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Select.Option key='white' value={GridColor.White}>White</Select.Option>
|
||||||
|
<Select.Option key='black' value={GridColor.Black}>Black</Select.Option>
|
||||||
|
<Select.Option key='red' value={GridColor.Red}>Red</Select.Option>
|
||||||
|
<Select.Option key='green' value={GridColor.Green}>Green</Select.Option>
|
||||||
|
<Select.Option key='blue' value={GridColor.Blue}>Blue</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Col>
|
||||||
|
<Col span={8} className='cvat-player-settings-grid-opacity'>
|
||||||
|
<Text className='cvat-text-color'> Grid opacity </Text>
|
||||||
|
<Slider
|
||||||
|
min={0}
|
||||||
|
max={100}
|
||||||
|
value={gridOpacity}
|
||||||
|
onChange={(value: number | [number, number]): void => {
|
||||||
|
onChangeGridOpacity(value as number);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text className='cvat-text-color'>{`${gridOpacity} %`}</Text>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row type='flex' justify='start'>
|
||||||
|
<Col>
|
||||||
|
<Row className='cvat-player-settings-reset-zoom'>
|
||||||
|
<Col className='cvat-player-settings-reset-zoom-checkbox'>
|
||||||
|
<Checkbox
|
||||||
|
className='cvat-text-color'
|
||||||
|
checked={resetZoom}
|
||||||
|
>
|
||||||
|
Reset zoom
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<Text type='secondary'> Fit image after changing frame </Text>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Col>
|
||||||
|
<Col offset={5}>
|
||||||
|
<Row className='cvat-player-settings-rotate-all'>
|
||||||
|
<Col className='cvat-player-settings-rotate-all-checkbox'>
|
||||||
|
<Checkbox
|
||||||
|
className='cvat-text-color'
|
||||||
|
checked={rotateAll}
|
||||||
|
onChange={(event: CheckboxChangeEvent): void => {
|
||||||
|
onSwitchRotateAll(event.target.checked);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Rotate all images
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<Text type='secondary'> Rotate all images simultaneously </Text>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row className='cvat-player-settings-brightness'>
|
||||||
|
<Col className='cvat-text-color'>
|
||||||
|
Brightness
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<Slider min={0} max={100} value={brightnessLevel} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row className='cvat-player-settings-contrast'>
|
||||||
|
<Col className='cvat-text-color'>
|
||||||
|
Contrast
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<Slider min={0} max={100} value={contrastLevel} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row className='cvat-player-settings-saturation'>
|
||||||
|
<Col className='cvat-text-color'>
|
||||||
|
Saturation
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<Slider min={0} max={100} value={saturationLevel} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
import './styles.scss';
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Row,
|
||||||
|
Col,
|
||||||
|
Tabs,
|
||||||
|
Icon,
|
||||||
|
Button,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import Text from 'antd/lib/typography/Text';
|
||||||
|
|
||||||
|
import { RouteComponentProps } from 'react-router';
|
||||||
|
import { withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
|
import WorkspaceSettingsContainer from '../../containers/settings-page/workspace-settings';
|
||||||
|
import PlayerSettingsContainer from '../../containers/settings-page/player-settings';
|
||||||
|
|
||||||
|
function SettingsPage(props: RouteComponentProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div className='cvat-settings-page'>
|
||||||
|
<Row type='flex' justify='center'>
|
||||||
|
<Col>
|
||||||
|
<Text className='cvat-title'> Settings </Text>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row type='flex' justify='center'>
|
||||||
|
<Col md={14} lg={12} xl={10} xxl={9}>
|
||||||
|
<Tabs
|
||||||
|
type='card'
|
||||||
|
tabBarStyle={{ marginBottom: '0px', marginLeft: '-1px' }}
|
||||||
|
>
|
||||||
|
<Tabs.TabPane
|
||||||
|
tab={
|
||||||
|
(
|
||||||
|
<span>
|
||||||
|
<Icon type='play-circle' />
|
||||||
|
<Text>Player</Text>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
key='player'
|
||||||
|
>
|
||||||
|
<PlayerSettingsContainer />
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane
|
||||||
|
tab={
|
||||||
|
(
|
||||||
|
<span>
|
||||||
|
<Icon type='laptop' />
|
||||||
|
<Text>Workspace</Text>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
key='workspace'
|
||||||
|
>
|
||||||
|
<WorkspaceSettingsContainer />
|
||||||
|
</Tabs.TabPane>
|
||||||
|
</Tabs>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row type='flex' justify='center'>
|
||||||
|
<Col md={14} lg={12} xl={10} xxl={9} className='cvat-settings-page-back-button-wrapper'>
|
||||||
|
<Button
|
||||||
|
className='cvat-settings-page-back-button'
|
||||||
|
type='primary'
|
||||||
|
onClick={(): void => {
|
||||||
|
props.history.goBack();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Go Back
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter(SettingsPage);
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
@import '../../base.scss';
|
||||||
|
|
||||||
|
.cvat-settings-page {
|
||||||
|
> div:nth-child(1) {
|
||||||
|
margin-top: 30px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-workspace-settings, .cvat-player-settings {
|
||||||
|
width: 100%;
|
||||||
|
height: max-content;
|
||||||
|
background: $background-color-1;
|
||||||
|
padding: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-player-settings-grid,
|
||||||
|
.cvat-workspace-settings-auto-save,
|
||||||
|
.cvat-workspace-settings-show-interpolated-checkbox {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-player-settings-grid-size,
|
||||||
|
.cvat-player-settings-grid-color,
|
||||||
|
.cvat-player-settings-grid-opacity,
|
||||||
|
.cvat-player-settings-step,
|
||||||
|
.cvat-player-settings-speed,
|
||||||
|
.cvat-player-settings-reset-zoom,
|
||||||
|
.cvat-player-settings-rotate-all,
|
||||||
|
.cvat-workspace-settings-show-interpolated,
|
||||||
|
.cvat-workspace-settings-aam-zoom-margin,
|
||||||
|
.cvat-workspace-settings-auto-save-interval {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-player-settings-grid-size,
|
||||||
|
.cvat-player-settings-grid-color,
|
||||||
|
.cvat-player-settings-grid-opacity {
|
||||||
|
display: grid;
|
||||||
|
justify-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-player-settings-grid-color {
|
||||||
|
> .ant-select {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-player-settings-grid-opacity {
|
||||||
|
> .ant-slider {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-player-settings-step,
|
||||||
|
.cvat-player-settings-speed {
|
||||||
|
> div {
|
||||||
|
display: grid;
|
||||||
|
justify-items: start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-player-settings-step > div > span > i {
|
||||||
|
vertical-align: -1em;
|
||||||
|
transform: scale(0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-player-settings-speed > div > .ant-select {
|
||||||
|
width: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-player-settings-brightness,
|
||||||
|
.cvat-player-settings-contrast,
|
||||||
|
.cvat-player-settings-saturation {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-settings-page-back-button {
|
||||||
|
width: 100px;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvat-settings-page-back-button-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Row,
|
||||||
|
Col,
|
||||||
|
Checkbox,
|
||||||
|
InputNumber,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import Text from 'antd/lib/typography/Text';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
autoSave: boolean;
|
||||||
|
autoSaveInterval: number;
|
||||||
|
aamZoomMargin: number;
|
||||||
|
showAllInterpolationTracks: boolean;
|
||||||
|
onSwitchAutoSave(enabled: boolean): void;
|
||||||
|
onChangeAutoSaveInterval(interval: number): void;
|
||||||
|
onChangeAAMZoomMargin(margin: number): void;
|
||||||
|
onSwitchShowingInterpolatedTracks(enabled: boolean): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function WorkspaceSettingsComponent(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
autoSave,
|
||||||
|
autoSaveInterval,
|
||||||
|
aamZoomMargin,
|
||||||
|
showAllInterpolationTracks,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='cvat-workspace-settings'>
|
||||||
|
<Row type='flex'>
|
||||||
|
<Col>
|
||||||
|
<Checkbox
|
||||||
|
className='cvat-text-color cvat-workspace-settings-auto-save'
|
||||||
|
checked={autoSave}
|
||||||
|
>
|
||||||
|
Enable auto save
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row type='flex'>
|
||||||
|
<Col className='cvat-workspace-settings-auto-save-interval'>
|
||||||
|
<Text type='secondary'> Auto save every </Text>
|
||||||
|
<InputNumber
|
||||||
|
min={5}
|
||||||
|
max={60}
|
||||||
|
step={1}
|
||||||
|
value={Math.round(autoSaveInterval / (60 * 1000))}
|
||||||
|
/>
|
||||||
|
<Text type='secondary'> minutes </Text>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row className='cvat-workspace-settings-show-interpolated'>
|
||||||
|
<Col className='cvat-workspace-settings-show-interpolated-checkbox'>
|
||||||
|
<Checkbox
|
||||||
|
className='cvat-text-color'
|
||||||
|
checked={showAllInterpolationTracks}
|
||||||
|
>
|
||||||
|
Show all interpolation tracks
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<Text type='secondary'> Show hidden interpolated objects in the side panel </Text>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row className='cvat-workspace-settings-aam-zoom-margin'>
|
||||||
|
<Col>
|
||||||
|
<Text className='cvat-text-color'> Attribute annotation mode (AAM) zoom margin </Text>
|
||||||
|
<InputNumber min={0} max={1000} value={aamZoomMargin} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import CanvasWrapperComponent from '../../../components/annotation-page/standard-workspace/canvas-wrapper';
|
||||||
|
|
||||||
|
import {
|
||||||
|
confirmCanvasReady,
|
||||||
|
} from '../../../actions/annotation-actions';
|
||||||
|
import {
|
||||||
|
GridColor,
|
||||||
|
CombinedState,
|
||||||
|
} from '../../../reducers/interfaces';
|
||||||
|
|
||||||
|
import { Canvas } from '../../../canvas';
|
||||||
|
|
||||||
|
interface StateToProps {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
jobInstance: any;
|
||||||
|
annotations: any[];
|
||||||
|
frameData: any;
|
||||||
|
grid: boolean;
|
||||||
|
gridSize: number;
|
||||||
|
gridColor: GridColor;
|
||||||
|
gridOpacity: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DispatchToProps {
|
||||||
|
onSetupCanvas(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: CombinedState): StateToProps {
|
||||||
|
const {
|
||||||
|
canvasInstance,
|
||||||
|
jobInstance,
|
||||||
|
frameData,
|
||||||
|
annotations,
|
||||||
|
} = state.annotation;
|
||||||
|
|
||||||
|
const {
|
||||||
|
grid,
|
||||||
|
gridSize,
|
||||||
|
gridColor,
|
||||||
|
gridOpacity,
|
||||||
|
} = state.settings.player;
|
||||||
|
|
||||||
|
return {
|
||||||
|
canvasInstance,
|
||||||
|
jobInstance,
|
||||||
|
frameData,
|
||||||
|
annotations,
|
||||||
|
grid,
|
||||||
|
gridSize,
|
||||||
|
gridColor,
|
||||||
|
gridOpacity,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps(dispatch: any): DispatchToProps {
|
||||||
|
return {
|
||||||
|
onSetupCanvas(): void {
|
||||||
|
dispatch(confirmCanvasReady());
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function CanvasWrapperContainer(props: StateToProps & DispatchToProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<CanvasWrapperComponent {...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
)(CanvasWrapperContainer);
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { Canvas } from '../../../canvas';
|
||||||
|
|
||||||
|
import ControlsSideBarComponent from '../../../components/annotation-page/standard-workspace/controls-side-bar';
|
||||||
|
import { CombinedState } from '../../../reducers/interfaces';
|
||||||
|
|
||||||
|
|
||||||
|
interface StateToProps {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
rotateAll: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: CombinedState): StateToProps {
|
||||||
|
const {
|
||||||
|
annotation,
|
||||||
|
settings,
|
||||||
|
} = state;
|
||||||
|
|
||||||
|
return {
|
||||||
|
rotateAll: settings.player.rotateAll,
|
||||||
|
canvasInstance: annotation.canvasInstance,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function StandardWorkspaceContainer(props: StateToProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<ControlsSideBarComponent {...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
)(StandardWorkspaceContainer);
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { Canvas } from '../../../canvas';
|
||||||
|
|
||||||
|
import StandardWorkspaceComponent from '../../../components/annotation-page/standard-workspace/standard-workspace';
|
||||||
|
import { CombinedState } from '../../../reducers/interfaces';
|
||||||
|
|
||||||
|
|
||||||
|
interface StateToProps {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: CombinedState): StateToProps {
|
||||||
|
const { annotation } = state;
|
||||||
|
return {
|
||||||
|
canvasInstance: annotation.canvasInstance,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function StandardWorkspaceContainer(props: StateToProps): JSX.Element {
|
||||||
|
const {
|
||||||
|
canvasInstance,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StandardWorkspaceComponent
|
||||||
|
canvasInstance={canvasInstance}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
)(StandardWorkspaceContainer);
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import {
|
||||||
|
changeFrameAsync,
|
||||||
|
switchPlay as switchPlayAction,
|
||||||
|
} from '../../../actions/annotation-actions';
|
||||||
|
|
||||||
|
import AnnotationTopBarComponent from '../../../components/annotation-page/top-bar/top-bar';
|
||||||
|
import { CombinedState } from '../../../reducers/interfaces';
|
||||||
|
|
||||||
|
interface StateToProps {
|
||||||
|
jobInstance: any;
|
||||||
|
frame: number;
|
||||||
|
frameStep: number;
|
||||||
|
playing: boolean;
|
||||||
|
canvasIsReady: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DispatchToProps {
|
||||||
|
onChangeFrame(frame: number, playing: boolean): void;
|
||||||
|
onSwitchPlay(playing: boolean): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: CombinedState): StateToProps {
|
||||||
|
const {
|
||||||
|
annotation,
|
||||||
|
settings,
|
||||||
|
} = state;
|
||||||
|
|
||||||
|
return {
|
||||||
|
jobInstance: annotation.jobInstance,
|
||||||
|
frame: annotation.frame as number, // is number when jobInstance specified
|
||||||
|
frameStep: settings.player.frameStep,
|
||||||
|
playing: annotation.playing,
|
||||||
|
canvasIsReady: annotation.canvasIsReady,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps(dispatch: any): DispatchToProps {
|
||||||
|
return {
|
||||||
|
onChangeFrame(frame: number, playing: boolean): void {
|
||||||
|
dispatch(changeFrameAsync(frame, playing));
|
||||||
|
},
|
||||||
|
onSwitchPlay(playing: boolean): void {
|
||||||
|
dispatch(switchPlayAction(playing));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function AnnotationTopBarContainer(props: StateToProps & DispatchToProps): JSX.Element {
|
||||||
|
const {
|
||||||
|
jobInstance,
|
||||||
|
frame,
|
||||||
|
frameStep,
|
||||||
|
playing,
|
||||||
|
canvasIsReady,
|
||||||
|
onChangeFrame,
|
||||||
|
onSwitchPlay,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AnnotationTopBarComponent
|
||||||
|
jobInstance={jobInstance}
|
||||||
|
frame={frame}
|
||||||
|
frameStep={frameStep}
|
||||||
|
playing={playing}
|
||||||
|
canvasIsReady={canvasIsReady}
|
||||||
|
onChangeFrame={onChangeFrame}
|
||||||
|
onSwitchPlay={onSwitchPlay}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
)(AnnotationTopBarContainer);
|
||||||
@ -0,0 +1,114 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import PlayerSettingsComponent from '../../components/settings-page/player-settings';
|
||||||
|
|
||||||
|
import {
|
||||||
|
switchRotateAll,
|
||||||
|
switchGrid,
|
||||||
|
changeGridSize,
|
||||||
|
changeGridColor,
|
||||||
|
changeGridOpacity,
|
||||||
|
} from '../../actions/settings-actions';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CombinedState,
|
||||||
|
FrameSpeed,
|
||||||
|
GridColor,
|
||||||
|
} from '../../reducers/interfaces';
|
||||||
|
|
||||||
|
interface StateToProps {
|
||||||
|
frameStep: number;
|
||||||
|
frameSpeed: FrameSpeed;
|
||||||
|
resetZoom: boolean;
|
||||||
|
rotateAll: boolean;
|
||||||
|
grid: boolean;
|
||||||
|
gridSize: number;
|
||||||
|
gridColor: GridColor;
|
||||||
|
gridOpacity: number;
|
||||||
|
brightnessLevel: number;
|
||||||
|
contrastLevel: number;
|
||||||
|
saturationLevel: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DispatchToProps {
|
||||||
|
onChangeFrameStep(step: number): void;
|
||||||
|
onChangeFrameSpeed(speed: FrameSpeed): void;
|
||||||
|
onSwitchResetZoom(enabled: boolean): void;
|
||||||
|
onSwitchRotateAll(rotateAll: boolean): void;
|
||||||
|
onSwitchGrid(grid: boolean): void;
|
||||||
|
onChangeGridSize(gridSize: number): void;
|
||||||
|
onChangeGridColor(gridColor: GridColor): void;
|
||||||
|
onChangeGridOpacity(gridOpacity: number): void;
|
||||||
|
onChangeBrightnessLevel(level: number): void;
|
||||||
|
onChangeContrastLevel(level: number): void;
|
||||||
|
onChangeSaturationLevel(level: number): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: CombinedState): StateToProps {
|
||||||
|
const { player } = state.settings;
|
||||||
|
return {
|
||||||
|
...player,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps(dispatch: any): DispatchToProps {
|
||||||
|
return {
|
||||||
|
// will be implemented
|
||||||
|
// eslint-disable-next-line
|
||||||
|
onChangeFrameStep(step: number): void {
|
||||||
|
|
||||||
|
},
|
||||||
|
// will be implemented
|
||||||
|
// eslint-disable-next-line
|
||||||
|
onChangeFrameSpeed(speed: FrameSpeed): void {
|
||||||
|
|
||||||
|
},
|
||||||
|
// will be implemented
|
||||||
|
// eslint-disable-next-line
|
||||||
|
onSwitchResetZoom(enabled: boolean): void {
|
||||||
|
|
||||||
|
},
|
||||||
|
onSwitchRotateAll(rotateAll: boolean): void {
|
||||||
|
dispatch(switchRotateAll(rotateAll));
|
||||||
|
},
|
||||||
|
onSwitchGrid(grid: boolean): void {
|
||||||
|
dispatch(switchGrid(grid));
|
||||||
|
},
|
||||||
|
onChangeGridSize(gridSize: number): void {
|
||||||
|
dispatch(changeGridSize(gridSize));
|
||||||
|
},
|
||||||
|
onChangeGridColor(gridColor: GridColor): void {
|
||||||
|
dispatch(changeGridColor(gridColor));
|
||||||
|
},
|
||||||
|
onChangeGridOpacity(gridOpacity: number): void {
|
||||||
|
dispatch(changeGridOpacity(gridOpacity));
|
||||||
|
},
|
||||||
|
// will be implemented
|
||||||
|
// eslint-disable-next-line
|
||||||
|
onChangeBrightnessLevel(level: number): void {
|
||||||
|
|
||||||
|
},
|
||||||
|
// will be implemented
|
||||||
|
// eslint-disable-next-line
|
||||||
|
onChangeContrastLevel(level: number): void {
|
||||||
|
|
||||||
|
},
|
||||||
|
// will be implemented
|
||||||
|
// eslint-disable-next-line
|
||||||
|
onChangeSaturationLevel(level: number): void {
|
||||||
|
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function PlayerSettingsContainer(props: StateToProps & DispatchToProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<PlayerSettingsComponent {...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
)(PlayerSettingsContainer);
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CombinedState,
|
||||||
|
} from '../../reducers/interfaces';
|
||||||
|
|
||||||
|
import WorkspaceSettingsComponent from '../../components/settings-page/workspace-settings';
|
||||||
|
|
||||||
|
interface StateToProps {
|
||||||
|
autoSave: boolean;
|
||||||
|
autoSaveInterval: number;
|
||||||
|
aamZoomMargin: number;
|
||||||
|
showAllInterpolationTracks: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DispatchToProps {
|
||||||
|
onSwitchAutoSave(enabled: boolean): void;
|
||||||
|
onChangeAutoSaveInterval(interval: number): void;
|
||||||
|
onChangeAAMZoomMargin(margin: number): void;
|
||||||
|
onSwitchShowingInterpolatedTracks(enabled: boolean): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: CombinedState): StateToProps {
|
||||||
|
const { workspace } = state.settings;
|
||||||
|
const {
|
||||||
|
autoSave,
|
||||||
|
autoSaveInterval,
|
||||||
|
aamZoomMargin,
|
||||||
|
showAllInterpolationTracks,
|
||||||
|
} = workspace;
|
||||||
|
|
||||||
|
return {
|
||||||
|
autoSave,
|
||||||
|
autoSaveInterval,
|
||||||
|
aamZoomMargin,
|
||||||
|
showAllInterpolationTracks,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps(): DispatchToProps {
|
||||||
|
return {
|
||||||
|
// will be implemented
|
||||||
|
// eslint-disable-next-line
|
||||||
|
onSwitchAutoSave(enabled: boolean): void {
|
||||||
|
|
||||||
|
},
|
||||||
|
// will be implemented
|
||||||
|
// eslint-disable-next-line
|
||||||
|
onChangeAutoSaveInterval(interval: number): void {
|
||||||
|
|
||||||
|
},
|
||||||
|
// will be implemented
|
||||||
|
// eslint-disable-next-line
|
||||||
|
onChangeAAMZoomMargin(margin: number): void {
|
||||||
|
|
||||||
|
},
|
||||||
|
// will be implemented
|
||||||
|
// eslint-disable-next-line
|
||||||
|
onSwitchShowingInterpolatedTracks(enabled: boolean): void {
|
||||||
|
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function WorkspaceSettingsContainer(props: StateToProps & DispatchToProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<WorkspaceSettingsComponent {...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
)(WorkspaceSettingsContainer);
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
import { AnyAction } from 'redux';
|
||||||
|
|
||||||
|
import { Canvas } from '../canvas';
|
||||||
|
|
||||||
|
import { AnnotationState } from './interfaces';
|
||||||
|
import { AnnotationActionTypes } from '../actions/annotation-actions';
|
||||||
|
|
||||||
|
const defaultState: AnnotationState = {
|
||||||
|
canvasInstance: new Canvas(),
|
||||||
|
canvasIsReady: false,
|
||||||
|
jobInstance: null,
|
||||||
|
frame: 0,
|
||||||
|
playing: false,
|
||||||
|
annotations: [],
|
||||||
|
frameData: null,
|
||||||
|
dataFetching: false,
|
||||||
|
jobFetching: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (state = defaultState, action: AnyAction): AnnotationState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case AnnotationActionTypes.GET_JOB: {
|
||||||
|
return {
|
||||||
|
...defaultState,
|
||||||
|
jobFetching: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case AnnotationActionTypes.GET_JOB_SUCCESS: {
|
||||||
|
return {
|
||||||
|
...defaultState,
|
||||||
|
jobFetching: false,
|
||||||
|
jobInstance: action.payload.jobInstance,
|
||||||
|
frame: action.payload.frame,
|
||||||
|
frameData: action.payload.frameData,
|
||||||
|
annotations: action.payload.annotations,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case AnnotationActionTypes.GET_JOB_FAILED: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
jobInstance: undefined,
|
||||||
|
jobFetching: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case AnnotationActionTypes.CHANGE_FRAME: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
frameData: null,
|
||||||
|
annotations: [],
|
||||||
|
dataFetching: true,
|
||||||
|
canvasIsReady: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case AnnotationActionTypes.CHANGE_FRAME_SUCCESS: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
frame: action.payload.frame,
|
||||||
|
annotations: action.payload.annotations,
|
||||||
|
frameData: action.payload.frameData,
|
||||||
|
dataFetching: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case AnnotationActionTypes.CHANGE_FRAME_FAILED: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
dataFetching: false,
|
||||||
|
}; // add notification if failed
|
||||||
|
}
|
||||||
|
case AnnotationActionTypes.SWITCH_PLAY: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
playing: action.payload.playing,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case AnnotationActionTypes.CONFIRM_CANVAS_READY: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
canvasIsReady: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
import { AnyAction } from 'redux';
|
||||||
|
import { SettingsActionTypes } from '../actions/settings-actions';
|
||||||
|
|
||||||
|
import {
|
||||||
|
SettingsState,
|
||||||
|
GridColor,
|
||||||
|
FrameSpeed,
|
||||||
|
} from './interfaces';
|
||||||
|
|
||||||
|
const defaultState: SettingsState = {
|
||||||
|
workspace: {
|
||||||
|
autoSave: false,
|
||||||
|
autoSaveInterval: 15 * 60 * 1000,
|
||||||
|
aamZoomMargin: 100,
|
||||||
|
showAllInterpolationTracks: false,
|
||||||
|
},
|
||||||
|
player: {
|
||||||
|
frameStep: 10,
|
||||||
|
frameSpeed: FrameSpeed.Usual,
|
||||||
|
resetZoom: false,
|
||||||
|
rotateAll: false,
|
||||||
|
grid: false,
|
||||||
|
gridSize: 100,
|
||||||
|
gridColor: GridColor.White,
|
||||||
|
gridOpacity: 0,
|
||||||
|
brightnessLevel: 50,
|
||||||
|
contrastLevel: 50,
|
||||||
|
saturationLevel: 50,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (state = defaultState, action: AnyAction): SettingsState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case SettingsActionTypes.SWITCH_ROTATE_ALL: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
player: {
|
||||||
|
...state.player,
|
||||||
|
rotateAll: action.payload.rotateAll,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case SettingsActionTypes.SWITCH_GRID: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
player: {
|
||||||
|
...state.player,
|
||||||
|
grid: action.payload.grid,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case SettingsActionTypes.CHANGE_GRID_SIZE: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
player: {
|
||||||
|
...state.player,
|
||||||
|
gridSize: action.payload.gridSize,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case SettingsActionTypes.CHANGE_GRID_COLOR: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
player: {
|
||||||
|
...state.player,
|
||||||
|
gridColor: action.payload.gridColor,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case SettingsActionTypes.CHANGE_GRID_OPACITY: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
player: {
|
||||||
|
...state.player,
|
||||||
|
gridOpacity: action.payload.gridOpacity,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue