Added option to display shape text always

main
Boris Sekachev 6 years ago
parent 361679dc76
commit 7d986d599e

@ -50,6 +50,11 @@ Canvas itself handles:
ZOOM_CANVAS = 'zoom_canvas', ZOOM_CANVAS = 'zoom_canvas',
} }
interface Configuration {
displayAllText?: boolean;
undefinedAttrValue?: string;
}
interface DrawData { interface DrawData {
enabled: boolean; enabled: boolean;
shapeType?: string; shapeType?: string;
@ -83,7 +88,6 @@ Canvas itself handles:
} }
interface Canvas { interface Canvas {
mode(): Mode;
html(): HTMLDivElement; html(): HTMLDivElement;
setZLayer(zLayer: number | null): void; setZLayer(zLayer: number | null): void;
setup(frameData: any, objectStates: any[]): void; setup(frameData: any, objectStates: any[]): void;
@ -103,7 +107,9 @@ Canvas itself handles:
dragCanvas(enable: boolean): void; dragCanvas(enable: boolean): void;
zoomCanvas(enable: boolean): void; zoomCanvas(enable: boolean): void;
mode(): void;
cancel(): void; cancel(): void;
configure(configuration: Configuration): void;
} }
``` ```
@ -189,4 +195,5 @@ Standard JS events are used.
| dragCanvas() | + | - | - | - | - | - | + | - | | dragCanvas() | + | - | - | - | - | - | + | - |
| zoomCanvas() | + | - | - | - | - | - | - | + | | zoomCanvas() | + | - | - | - | - | - | - | + |
| cancel() | - | + | + | + | + | + | + | + | | cancel() | - | + | + | + | + | + | + | + |
| configure() | + | - | - | - | - | - | - | - |
| setZLayer() | + | + | + | + | + | + | + | + | | setZLayer() | + | + | + | + | + | + | + | + |

@ -11,6 +11,7 @@ import {
CanvasModel, CanvasModel,
CanvasModelImpl, CanvasModelImpl,
RectDrawingMethod, RectDrawingMethod,
Configuration,
} from './canvasModel'; } from './canvasModel';
import { import {
@ -54,6 +55,7 @@ interface Canvas {
mode(): void; mode(): void;
cancel(): void; cancel(): void;
configure(configuration: Configuration): void;
} }
class CanvasImpl implements Canvas { class CanvasImpl implements Canvas {
@ -141,11 +143,16 @@ class CanvasImpl implements Canvas {
public cancel(): void { public cancel(): void {
this.model.cancel(); this.model.cancel();
} }
public configure(configuration: Configuration): void {
this.model.configure(configuration);
}
} }
export { export {
CanvasImpl as Canvas, CanvasImpl as Canvas,
CanvasVersion, CanvasVersion,
Configuration,
RectDrawingMethod, RectDrawingMethod,
Mode as CanvasMode, Mode as CanvasMode,
}; };

@ -36,7 +36,6 @@ export interface CanvasController {
enableDrag(x: number, y: number): void; enableDrag(x: number, y: number): void;
drag(x: number, y: number): void; drag(x: number, y: number): void;
disableDrag(): void; disableDrag(): void;
fit(): void; fit(): void;
} }

@ -46,6 +46,11 @@ export enum RectDrawingMethod {
EXTREME_POINTS = 'By 4 points' EXTREME_POINTS = 'By 4 points'
} }
export interface Configuration {
displayAllText?: boolean;
undefinedAttrValue?: string;
}
export interface DrawData { export interface DrawData {
enabled: boolean; enabled: boolean;
shapeType?: string; shapeType?: string;
@ -100,6 +105,7 @@ export enum UpdateReasons {
CANCEL = 'cancel', CANCEL = 'cancel',
DRAG_CANVAS = 'drag_canvas', DRAG_CANVAS = 'drag_canvas',
ZOOM_CANVAS = 'ZOOM_CANVAS', ZOOM_CANVAS = 'ZOOM_CANVAS',
CONFIG_UPDATED = 'config_updated',
} }
export enum Mode { export enum Mode {
@ -126,6 +132,7 @@ export interface CanvasModel {
readonly mergeData: MergeData; readonly mergeData: MergeData;
readonly splitData: SplitData; readonly splitData: SplitData;
readonly groupData: GroupData; readonly groupData: GroupData;
readonly configuration: Configuration;
readonly selected: any; readonly selected: any;
geometry: Geometry; geometry: Geometry;
mode: Mode; mode: Mode;
@ -151,6 +158,7 @@ export interface CanvasModel {
dragCanvas(enable: boolean): void; dragCanvas(enable: boolean): void;
zoomCanvas(enable: boolean): void; zoomCanvas(enable: boolean): void;
configure(configuration: Configuration): void;
cancel(): void; cancel(): void;
} }
@ -159,6 +167,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
activeElement: ActiveElement; activeElement: ActiveElement;
angle: number; angle: number;
canvasSize: Size; canvasSize: Size;
configuration: Configuration;
image: Image | null; image: Image | null;
imageID: number | null; imageID: number | null;
imageOffset: number; imageOffset: number;
@ -191,6 +200,10 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
height: 0, height: 0,
width: 0, width: 0,
}, },
configuration: {
displayAllText: false,
undefinedAttrValue: '',
},
image: null, image: null,
imageID: null, imageID: null,
imageOffset: 0, imageOffset: 0,
@ -485,10 +498,30 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
this.data.selected = null; this.data.selected = null;
} }
public configure(configuration: Configuration): void {
if (this.data.mode !== Mode.IDLE) {
throw Error(`Canvas is busy. Action: ${this.data.mode}`);
}
if (typeof (configuration.displayAllText) !== 'undefined') {
this.data.configuration.displayAllText = configuration.displayAllText;
}
if (typeof (configuration.undefinedAttrValue) !== 'undefined') {
this.data.configuration.undefinedAttrValue = configuration.undefinedAttrValue;
}
this.notify(UpdateReasons.CONFIG_UPDATED);
}
public cancel(): void { public cancel(): void {
this.notify(UpdateReasons.CANCEL); this.notify(UpdateReasons.CANCEL);
} }
public get configuration(): Configuration {
return { ...this.data.configuration };
}
public get geometry(): Geometry { public get geometry(): Geometry {
return { return {
angle: this.data.angle, angle: this.data.angle,

@ -36,6 +36,7 @@ import {
GroupData, GroupData,
Mode, Mode,
Size, Size,
Configuration,
} from './canvasModel'; } from './canvasModel';
export interface CanvasView { export interface CanvasView {
@ -65,6 +66,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
private groupHandler: GroupHandler; private groupHandler: GroupHandler;
private zoomHandler: ZoomHandler; private zoomHandler: ZoomHandler;
private activeElement: ActiveElement; private activeElement: ActiveElement;
private configuration: Configuration;
private set mode(value: Mode) { private set mode(value: Mode) {
this.controller.mode = value; this.controller.mode = value;
@ -538,6 +540,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
clientID: null, clientID: null,
attributeID: null, attributeID: null,
}; };
this.configuration = model.configuration;
this.mode = Mode.IDLE; this.mode = Mode.IDLE;
// Create HTML elements // Create HTML elements
@ -702,7 +705,11 @@ export class CanvasViewImpl implements CanvasView, Listener {
public notify(model: CanvasModel & Master, reason: UpdateReasons): void { public notify(model: CanvasModel & Master, reason: UpdateReasons): void {
this.geometry = this.controller.geometry; this.geometry = this.controller.geometry;
if (reason === UpdateReasons.IMAGE_CHANGED) { if (reason === UpdateReasons.CONFIG_UPDATED) {
this.configuration = model.configuration;
this.setupObjects([]);
this.setupObjects(model.objects);
} else if (reason === UpdateReasons.IMAGE_CHANGED) {
const { image } = model; const { image } = model;
if (!image) { if (!image) {
this.loadingAnimation.classList.remove('cvat_canvas_hidden'); this.loadingAnimation.classList.remove('cvat_canvas_hidden');
@ -987,6 +994,8 @@ export class CanvasViewImpl implements CanvasView, Listener {
} }
private addObjects(states: any[], translate: (points: number[]) => number[]): void { private addObjects(states: any[], translate: (points: number[]) => number[]): void {
const { displayAllText } = this.configuration;
for (const state of states) { for (const state of states) {
if (state.objectType === 'tag') { if (state.objectType === 'tag') {
this.addTag(state); this.addTag(state);
@ -1030,6 +1039,14 @@ export class CanvasViewImpl implements CanvasView, Listener {
}, },
})); }));
}); });
if (displayAllText) {
this.svgTexts[state.clientID] = this.addText(state);
this.updateTextPosition(
this.svgTexts[state.clientID],
this.svgShapes[state.clientID],
);
}
} }
this.saveState(state); this.saveState(state);
@ -1078,6 +1095,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
private deactivateShape(): void { private deactivateShape(): void {
if (this.activeElement.clientID !== null) { if (this.activeElement.clientID !== null) {
const { displayAllText } = this.configuration;
const { clientID } = this.activeElement; const { clientID } = this.activeElement;
const drawnState = this.drawnStates[clientID]; const drawnState = this.drawnStates[clientID];
const shape = this.svgShapes[clientID]; const shape = this.svgShapes[clientID];
@ -1101,7 +1119,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
// TODO: Hide text only if it is hidden by settings // TODO: Hide text only if it is hidden by settings
const text = this.svgTexts[clientID]; const text = this.svgTexts[clientID];
if (text) { if (text && !displayAllText) {
text.remove(); text.remove();
delete this.svgTexts[clientID]; delete this.svgTexts[clientID];
} }
@ -1347,6 +1365,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
} }
private addText(state: any): SVG.Text { private addText(state: any): SVG.Text {
const { undefinedAttrValue } = this.configuration;
const { label, clientID, attributes } = state; const { label, clientID, attributes } = state;
const attrNames = label.attributes.reduce((acc: any, val: any): void => { const attrNames = label.attributes.reduce((acc: any, val: any): void => {
acc[val.id] = val.name; acc[val.id] = val.name;
@ -1356,7 +1375,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
return this.adoptedText.text((block): void => { return this.adoptedText.text((block): void => {
block.tspan(`${label.name} ${clientID}`).style('text-transform', 'uppercase'); block.tspan(`${label.name} ${clientID}`).style('text-transform', 'uppercase');
for (const attrID of Object.keys(attributes)) { for (const attrID of Object.keys(attributes)) {
const value = attributes[attrID] === consts.UNDEFINED_ATTRIBUTE_VALUE const value = attributes[attrID] === undefinedAttrValue
? '' : attributes[attrID]; ? '' : attributes[attrID];
block.tspan(`${attrNames[attrID]}: ${value}`).attr({ block.tspan(`${attrNames[attrID]}: ${value}`).attr({
attrID, attrID,

@ -28,6 +28,7 @@ export enum SettingsActionTypes {
CHANGE_AUTO_SAVE_INTERVAL = 'CHANGE_AUTO_SAVE_INTERVAL', CHANGE_AUTO_SAVE_INTERVAL = 'CHANGE_AUTO_SAVE_INTERVAL',
CHANGE_AAM_ZOOM_MARGIN = 'CHANGE_AAM_ZOOM_MARGIN', CHANGE_AAM_ZOOM_MARGIN = 'CHANGE_AAM_ZOOM_MARGIN',
SWITCH_SHOWNIG_INTERPOLATED_TRACKS = 'SWITCH_SHOWNIG_INTERPOLATED_TRACKS', SWITCH_SHOWNIG_INTERPOLATED_TRACKS = 'SWITCH_SHOWNIG_INTERPOLATED_TRACKS',
SWITCH_SHOWING_OBJECTS_TEXT_ALWAYS = 'SWITCH_SHOWING_OBJECTS_TEXT_ALWAYS',
} }
export function changeShapesOpacity(opacity: number): AnyAction { export function changeShapesOpacity(opacity: number): AnyAction {
@ -200,3 +201,12 @@ export function switchShowingInterpolatedTracks(showAllInterpolationTracks: bool
}, },
}; };
} }
export function switchShowingObjectsTextAlways(showObjectsTextAlways: boolean): AnyAction {
return {
type: SettingsActionTypes.SWITCH_SHOWING_OBJECTS_TEXT_ALWAYS,
payload: {
showObjectsTextAlways,
},
};
}

@ -21,6 +21,7 @@ import {
import { LogType } from 'cvat-logger'; import { LogType } from 'cvat-logger';
import { Canvas } from 'cvat-canvas'; import { Canvas } from 'cvat-canvas';
import getCore from 'cvat-core'; import getCore from 'cvat-core';
import consts from 'consts';
const cvat = getCore(); const cvat = getCore();
@ -58,6 +59,7 @@ interface Props {
contextVisible: boolean; contextVisible: boolean;
contextType: ContextMenuType; contextType: ContextMenuType;
aamZoomMargin: number; aamZoomMargin: number;
showObjectsTextAlways: boolean;
workspace: Workspace; workspace: Workspace;
keyMap: Record<string, ExtendedKeyMapOptions>; keyMap: Record<string, ExtendedKeyMapOptions>;
onSetupCanvas: () => void; onSetupCanvas: () => void;
@ -91,6 +93,7 @@ interface Props {
export default class CanvasWrapperComponent extends React.PureComponent<Props> { export default class CanvasWrapperComponent extends React.PureComponent<Props> {
public componentDidMount(): void { public componentDidMount(): void {
const { const {
showObjectsTextAlways,
canvasInstance, canvasInstance,
curZLayer, curZLayer,
} = this.props; } = this.props;
@ -101,7 +104,12 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
.getElementsByClassName('cvat-canvas-container'); .getElementsByClassName('cvat-canvas-container');
wrapper.appendChild(canvasInstance.html()); wrapper.appendChild(canvasInstance.html());
canvasInstance.configure({
undefinedAttrValue: consts.UNDEFINED_ATTRIBUTE_VALUE,
displayAllText: showObjectsTextAlways,
});
canvasInstance.setZLayer(curZLayer); canvasInstance.setZLayer(curZLayer);
this.initialSetup(); this.initialSetup();
this.updateCanvas(); this.updateCanvas();
} }
@ -128,8 +136,16 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
saturationLevel, saturationLevel,
workspace, workspace,
frameFetching, frameFetching,
showObjectsTextAlways,
} = this.props; } = this.props;
if (prevProps.showObjectsTextAlways !== showObjectsTextAlways) {
canvasInstance.configure({
undefinedAttrValue: consts.UNDEFINED_ATTRIBUTE_VALUE,
displayAllText: showObjectsTextAlways,
});
}
if (prevProps.sidebarCollapsed !== sidebarCollapsed) { if (prevProps.sidebarCollapsed !== sidebarCollapsed) {
const [sidebar] = window.document.getElementsByClassName('cvat-objects-sidebar'); const [sidebar] = window.document.getElementsByClassName('cvat-objects-sidebar');
if (sidebar) { if (sidebar) {

@ -20,6 +20,8 @@
.cvat-player-settings-grid, .cvat-player-settings-grid,
.cvat-workspace-settings-auto-save, .cvat-workspace-settings-auto-save,
.cvat-workspace-settings-show-text-always,
.cvat-workspace-settings-show-text-always-checkbox,
.cvat-workspace-settings-show-interpolated-checkbox { .cvat-workspace-settings-show-interpolated-checkbox {
margin-bottom: 10px; margin-bottom: 10px;
} }
@ -31,6 +33,7 @@
.cvat-player-settings-speed, .cvat-player-settings-speed,
.cvat-player-settings-reset-zoom, .cvat-player-settings-reset-zoom,
.cvat-player-settings-rotate-all, .cvat-player-settings-rotate-all,
.cvat-workspace-settings-show-text-always,
.cvat-workspace-settings-show-interpolated, .cvat-workspace-settings-show-interpolated,
.cvat-workspace-settings-aam-zoom-margin, .cvat-workspace-settings-aam-zoom-margin,
.cvat-workspace-settings-auto-save-interval { .cvat-workspace-settings-auto-save-interval {

@ -16,10 +16,12 @@ interface Props {
autoSaveInterval: number; autoSaveInterval: number;
aamZoomMargin: number; aamZoomMargin: number;
showAllInterpolationTracks: boolean; showAllInterpolationTracks: boolean;
showObjectsTextAlways: boolean;
onSwitchAutoSave(enabled: boolean): void; onSwitchAutoSave(enabled: boolean): void;
onChangeAutoSaveInterval(interval: number): void; onChangeAutoSaveInterval(interval: number): void;
onChangeAAMZoomMargin(margin: number): void; onChangeAAMZoomMargin(margin: number): void;
onSwitchShowingInterpolatedTracks(enabled: boolean): void; onSwitchShowingInterpolatedTracks(enabled: boolean): void;
onSwitchShowingObjectsTextAlways(enabled: boolean): void;
} }
export default function WorkspaceSettingsComponent(props: Props): JSX.Element { export default function WorkspaceSettingsComponent(props: Props): JSX.Element {
@ -28,10 +30,12 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element {
autoSaveInterval, autoSaveInterval,
aamZoomMargin, aamZoomMargin,
showAllInterpolationTracks, showAllInterpolationTracks,
showObjectsTextAlways,
onSwitchAutoSave, onSwitchAutoSave,
onChangeAutoSaveInterval, onChangeAutoSaveInterval,
onChangeAAMZoomMargin, onChangeAAMZoomMargin,
onSwitchShowingInterpolatedTracks, onSwitchShowingInterpolatedTracks,
onSwitchShowingObjectsTextAlways,
} = props; } = props;
const minAutoSaveInterval = 5; const minAutoSaveInterval = 5;
@ -93,6 +97,22 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element {
<Text type='secondary'> Show hidden interpolated objects in the side panel </Text> <Text type='secondary'> Show hidden interpolated objects in the side panel </Text>
</Col> </Col>
</Row> </Row>
<Row className='cvat-workspace-settings-show-text-always'>
<Col className='cvat-workspace-settings-show-text-always-checkbox'>
<Checkbox
className='cvat-text-color'
checked={showObjectsTextAlways}
onChange={(event: CheckboxChangeEvent): void => {
onSwitchShowingObjectsTextAlways(event.target.checked);
}}
>
Always show object details
</Checkbox>
</Col>
<Col>
<Text type='secondary'> Show text for an object on the canvas not only when the object is activated </Text>
</Col>
</Row>
<Row className='cvat-workspace-settings-aam-zoom-margin'> <Row className='cvat-workspace-settings-aam-zoom-margin'>
<Col> <Col>
<Text className='cvat-text-color'> Attribute annotation mode (AAM) zoom margin </Text> <Text className='cvat-text-color'> Attribute annotation mode (AAM) zoom margin </Text>

@ -73,6 +73,7 @@ interface StateToProps {
saturationLevel: number; saturationLevel: number;
resetZoom: boolean; resetZoom: boolean;
aamZoomMargin: number; aamZoomMargin: number;
showObjectsTextAlways: boolean;
workspace: Workspace; workspace: Workspace;
minZLayer: number; minZLayer: number;
maxZLayer: number; maxZLayer: number;
@ -163,6 +164,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
}, },
workspace: { workspace: {
aamZoomMargin, aamZoomMargin,
showObjectsTextAlways,
}, },
shapes: { shapes: {
opacity, opacity,
@ -203,6 +205,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
saturationLevel, saturationLevel,
resetZoom, resetZoom,
aamZoomMargin, aamZoomMargin,
showObjectsTextAlways,
curZLayer, curZLayer,
minZLayer, minZLayer,
maxZLayer, maxZLayer,

@ -10,11 +10,10 @@ import {
changeAutoSaveInterval, changeAutoSaveInterval,
changeAAMZoomMargin, changeAAMZoomMargin,
switchShowingInterpolatedTracks, switchShowingInterpolatedTracks,
switchShowingObjectsTextAlways,
} from 'actions/settings-actions'; } from 'actions/settings-actions';
import { import { CombinedState } from 'reducers/interfaces';
CombinedState,
} from 'reducers/interfaces';
import WorkspaceSettingsComponent from 'components/settings-page/workspace-settings'; import WorkspaceSettingsComponent from 'components/settings-page/workspace-settings';
@ -23,6 +22,7 @@ interface StateToProps {
autoSaveInterval: number; autoSaveInterval: number;
aamZoomMargin: number; aamZoomMargin: number;
showAllInterpolationTracks: boolean; showAllInterpolationTracks: boolean;
showObjectsTextAlways: boolean;
} }
interface DispatchToProps { interface DispatchToProps {
@ -30,6 +30,7 @@ interface DispatchToProps {
onChangeAutoSaveInterval(interval: number): void; onChangeAutoSaveInterval(interval: number): void;
onChangeAAMZoomMargin(margin: number): void; onChangeAAMZoomMargin(margin: number): void;
onSwitchShowingInterpolatedTracks(enabled: boolean): void; onSwitchShowingInterpolatedTracks(enabled: boolean): void;
onSwitchShowingObjectsTextAlways(enabled: boolean): void;
} }
function mapStateToProps(state: CombinedState): StateToProps { function mapStateToProps(state: CombinedState): StateToProps {
@ -39,6 +40,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
autoSaveInterval, autoSaveInterval,
aamZoomMargin, aamZoomMargin,
showAllInterpolationTracks, showAllInterpolationTracks,
showObjectsTextAlways,
} = workspace; } = workspace;
return { return {
@ -46,6 +48,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
autoSaveInterval, autoSaveInterval,
aamZoomMargin, aamZoomMargin,
showAllInterpolationTracks, showAllInterpolationTracks,
showObjectsTextAlways,
}; };
} }
@ -63,6 +66,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
onSwitchShowingInterpolatedTracks(enabled: boolean): void { onSwitchShowingInterpolatedTracks(enabled: boolean): void {
dispatch(switchShowingInterpolatedTracks(enabled)); dispatch(switchShowingInterpolatedTracks(enabled));
}, },
onSwitchShowingObjectsTextAlways(enabled: boolean): void {
dispatch(switchShowingObjectsTextAlways(enabled));
},
}; };
} }

@ -426,6 +426,7 @@ export interface WorkspaceSettingsState {
autoSave: boolean; autoSave: boolean;
autoSaveInterval: number; // in ms autoSaveInterval: number; // in ms
aamZoomMargin: number; aamZoomMargin: number;
showObjectsTextAlways: boolean;
showAllInterpolationTracks: boolean; showAllInterpolationTracks: boolean;
} }

@ -27,6 +27,7 @@ const defaultState: SettingsState = {
autoSave: false, autoSave: false,
autoSaveInterval: 15 * 60 * 1000, autoSaveInterval: 15 * 60 * 1000,
aamZoomMargin: 100, aamZoomMargin: 100,
showObjectsTextAlways: false,
showAllInterpolationTracks: false, showAllInterpolationTracks: false,
}, },
player: { player: {
@ -217,6 +218,15 @@ export default (state = defaultState, action: AnyAction): SettingsState => {
}, },
}; };
} }
case SettingsActionTypes.SWITCH_SHOWING_OBJECTS_TEXT_ALWAYS: {
return {
...state,
workspace: {
...state.workspace,
showObjectsTextAlways: action.payload.showObjectsTextAlways,
},
};
}
case AnnotationActionTypes.GET_JOB_SUCCESS: { case AnnotationActionTypes.GET_JOB_SUCCESS: {
const { job } = action.payload; const { job } = action.payload;

Loading…
Cancel
Save