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