From 7d986d599eb3e85fd06e55717678b4f19ae22df5 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 2 Apr 2020 22:14:27 +0300 Subject: [PATCH] Added option to display shape text always --- cvat-canvas/README.md | 9 ++++- cvat-canvas/src/typescript/canvas.ts | 7 ++++ .../src/typescript/canvasController.ts | 1 - cvat-canvas/src/typescript/canvasModel.ts | 33 +++++++++++++++++++ cvat-canvas/src/typescript/canvasView.ts | 25 ++++++++++++-- cvat-ui/src/actions/settings-actions.ts | 10 ++++++ .../standard-workspace/canvas-wrapper.tsx | 16 +++++++++ .../src/components/settings-page/styles.scss | 3 ++ .../settings-page/workspace-settings.tsx | 20 +++++++++++ .../standard-workspace/canvas-wrapper.tsx | 3 ++ .../settings-page/workspace-settings.tsx | 12 +++++-- cvat-ui/src/reducers/interfaces.ts | 1 + cvat-ui/src/reducers/settings-reducer.ts | 10 ++++++ 13 files changed, 142 insertions(+), 8 deletions(-) diff --git a/cvat-canvas/README.md b/cvat-canvas/README.md index 7fe60491..ea6bfa0e 100644 --- a/cvat-canvas/README.md +++ b/cvat-canvas/README.md @@ -50,6 +50,11 @@ Canvas itself handles: ZOOM_CANVAS = 'zoom_canvas', } + interface Configuration { + displayAllText?: boolean; + undefinedAttrValue?: string; + } + interface DrawData { enabled: boolean; shapeType?: string; @@ -83,7 +88,6 @@ Canvas itself handles: } interface Canvas { - mode(): Mode; html(): HTMLDivElement; setZLayer(zLayer: number | null): void; setup(frameData: any, objectStates: any[]): void; @@ -103,7 +107,9 @@ Canvas itself handles: dragCanvas(enable: boolean): void; zoomCanvas(enable: boolean): void; + mode(): void; cancel(): void; + configure(configuration: Configuration): void; } ``` @@ -189,4 +195,5 @@ Standard JS events are used. | dragCanvas() | + | - | - | - | - | - | + | - | | zoomCanvas() | + | - | - | - | - | - | - | + | | cancel() | - | + | + | + | + | + | + | + | +| configure() | + | - | - | - | - | - | - | - | | setZLayer() | + | + | + | + | + | + | + | + | diff --git a/cvat-canvas/src/typescript/canvas.ts b/cvat-canvas/src/typescript/canvas.ts index eef1de40..c601034f 100644 --- a/cvat-canvas/src/typescript/canvas.ts +++ b/cvat-canvas/src/typescript/canvas.ts @@ -11,6 +11,7 @@ import { CanvasModel, CanvasModelImpl, RectDrawingMethod, + Configuration, } from './canvasModel'; import { @@ -54,6 +55,7 @@ interface Canvas { mode(): void; cancel(): void; + configure(configuration: Configuration): void; } class CanvasImpl implements Canvas { @@ -141,11 +143,16 @@ class CanvasImpl implements Canvas { public cancel(): void { this.model.cancel(); } + + public configure(configuration: Configuration): void { + this.model.configure(configuration); + } } export { CanvasImpl as Canvas, CanvasVersion, + Configuration, RectDrawingMethod, Mode as CanvasMode, }; diff --git a/cvat-canvas/src/typescript/canvasController.ts b/cvat-canvas/src/typescript/canvasController.ts index 7fc555c6..179f9b32 100644 --- a/cvat-canvas/src/typescript/canvasController.ts +++ b/cvat-canvas/src/typescript/canvasController.ts @@ -36,7 +36,6 @@ export interface CanvasController { enableDrag(x: number, y: number): void; drag(x: number, y: number): void; disableDrag(): void; - fit(): void; } diff --git a/cvat-canvas/src/typescript/canvasModel.ts b/cvat-canvas/src/typescript/canvasModel.ts index 66a2717b..888b5499 100644 --- a/cvat-canvas/src/typescript/canvasModel.ts +++ b/cvat-canvas/src/typescript/canvasModel.ts @@ -46,6 +46,11 @@ export enum RectDrawingMethod { EXTREME_POINTS = 'By 4 points' } +export interface Configuration { + displayAllText?: boolean; + undefinedAttrValue?: string; +} + export interface DrawData { enabled: boolean; shapeType?: string; @@ -100,6 +105,7 @@ export enum UpdateReasons { CANCEL = 'cancel', DRAG_CANVAS = 'drag_canvas', ZOOM_CANVAS = 'ZOOM_CANVAS', + CONFIG_UPDATED = 'config_updated', } export enum Mode { @@ -126,6 +132,7 @@ export interface CanvasModel { readonly mergeData: MergeData; readonly splitData: SplitData; readonly groupData: GroupData; + readonly configuration: Configuration; readonly selected: any; geometry: Geometry; mode: Mode; @@ -151,6 +158,7 @@ export interface CanvasModel { dragCanvas(enable: boolean): void; zoomCanvas(enable: boolean): void; + configure(configuration: Configuration): void; cancel(): void; } @@ -159,6 +167,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { activeElement: ActiveElement; angle: number; canvasSize: Size; + configuration: Configuration; image: Image | null; imageID: number | null; imageOffset: number; @@ -191,6 +200,10 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { height: 0, width: 0, }, + configuration: { + displayAllText: false, + undefinedAttrValue: '', + }, image: null, imageID: null, imageOffset: 0, @@ -485,10 +498,30 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { 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 { this.notify(UpdateReasons.CANCEL); } + public get configuration(): Configuration { + return { ...this.data.configuration }; + } + public get geometry(): Geometry { return { angle: this.data.angle, diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 75963144..bd460be6 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -36,6 +36,7 @@ import { GroupData, Mode, Size, + Configuration, } from './canvasModel'; export interface CanvasView { @@ -65,6 +66,7 @@ export class CanvasViewImpl implements CanvasView, Listener { private groupHandler: GroupHandler; private zoomHandler: ZoomHandler; private activeElement: ActiveElement; + private configuration: Configuration; private set mode(value: Mode) { this.controller.mode = value; @@ -538,6 +540,7 @@ export class CanvasViewImpl implements CanvasView, Listener { clientID: null, attributeID: null, }; + this.configuration = model.configuration; this.mode = Mode.IDLE; // Create HTML elements @@ -702,7 +705,11 @@ export class CanvasViewImpl implements CanvasView, Listener { public notify(model: CanvasModel & Master, reason: UpdateReasons): void { 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; if (!image) { 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 { + const { displayAllText } = this.configuration; + for (const state of states) { if (state.objectType === 'tag') { 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); @@ -1078,6 +1095,7 @@ export class CanvasViewImpl implements CanvasView, Listener { private deactivateShape(): void { if (this.activeElement.clientID !== null) { + const { displayAllText } = this.configuration; const { clientID } = this.activeElement; const drawnState = this.drawnStates[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 const text = this.svgTexts[clientID]; - if (text) { + if (text && !displayAllText) { text.remove(); delete this.svgTexts[clientID]; } @@ -1347,6 +1365,7 @@ export class CanvasViewImpl implements CanvasView, Listener { } private addText(state: any): SVG.Text { + const { undefinedAttrValue } = this.configuration; const { label, clientID, attributes } = state; const attrNames = label.attributes.reduce((acc: any, val: any): void => { acc[val.id] = val.name; @@ -1356,7 +1375,7 @@ export class CanvasViewImpl implements CanvasView, Listener { return this.adoptedText.text((block): void => { block.tspan(`${label.name} ${clientID}`).style('text-transform', 'uppercase'); for (const attrID of Object.keys(attributes)) { - const value = attributes[attrID] === consts.UNDEFINED_ATTRIBUTE_VALUE + const value = attributes[attrID] === undefinedAttrValue ? '' : attributes[attrID]; block.tspan(`${attrNames[attrID]}: ${value}`).attr({ attrID, diff --git a/cvat-ui/src/actions/settings-actions.ts b/cvat-ui/src/actions/settings-actions.ts index c14c2db2..03b56cc2 100644 --- a/cvat-ui/src/actions/settings-actions.ts +++ b/cvat-ui/src/actions/settings-actions.ts @@ -28,6 +28,7 @@ export enum SettingsActionTypes { CHANGE_AUTO_SAVE_INTERVAL = 'CHANGE_AUTO_SAVE_INTERVAL', CHANGE_AAM_ZOOM_MARGIN = 'CHANGE_AAM_ZOOM_MARGIN', SWITCH_SHOWNIG_INTERPOLATED_TRACKS = 'SWITCH_SHOWNIG_INTERPOLATED_TRACKS', + SWITCH_SHOWING_OBJECTS_TEXT_ALWAYS = 'SWITCH_SHOWING_OBJECTS_TEXT_ALWAYS', } 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, + }, + }; +} diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx index 038157e2..4f38f3db 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx @@ -21,6 +21,7 @@ import { import { LogType } from 'cvat-logger'; import { Canvas } from 'cvat-canvas'; import getCore from 'cvat-core'; +import consts from 'consts'; const cvat = getCore(); @@ -58,6 +59,7 @@ interface Props { contextVisible: boolean; contextType: ContextMenuType; aamZoomMargin: number; + showObjectsTextAlways: boolean; workspace: Workspace; keyMap: Record; onSetupCanvas: () => void; @@ -91,6 +93,7 @@ interface Props { export default class CanvasWrapperComponent extends React.PureComponent { public componentDidMount(): void { const { + showObjectsTextAlways, canvasInstance, curZLayer, } = this.props; @@ -101,7 +104,12 @@ export default class CanvasWrapperComponent extends React.PureComponent { .getElementsByClassName('cvat-canvas-container'); wrapper.appendChild(canvasInstance.html()); + canvasInstance.configure({ + undefinedAttrValue: consts.UNDEFINED_ATTRIBUTE_VALUE, + displayAllText: showObjectsTextAlways, + }); canvasInstance.setZLayer(curZLayer); + this.initialSetup(); this.updateCanvas(); } @@ -128,8 +136,16 @@ export default class CanvasWrapperComponent extends React.PureComponent { saturationLevel, workspace, frameFetching, + showObjectsTextAlways, } = this.props; + if (prevProps.showObjectsTextAlways !== showObjectsTextAlways) { + canvasInstance.configure({ + undefinedAttrValue: consts.UNDEFINED_ATTRIBUTE_VALUE, + displayAllText: showObjectsTextAlways, + }); + } + if (prevProps.sidebarCollapsed !== sidebarCollapsed) { const [sidebar] = window.document.getElementsByClassName('cvat-objects-sidebar'); if (sidebar) { diff --git a/cvat-ui/src/components/settings-page/styles.scss b/cvat-ui/src/components/settings-page/styles.scss index ef4b005a..d54c7206 100644 --- a/cvat-ui/src/components/settings-page/styles.scss +++ b/cvat-ui/src/components/settings-page/styles.scss @@ -20,6 +20,8 @@ .cvat-player-settings-grid, .cvat-workspace-settings-auto-save, +.cvat-workspace-settings-show-text-always, +.cvat-workspace-settings-show-text-always-checkbox, .cvat-workspace-settings-show-interpolated-checkbox { margin-bottom: 10px; } @@ -31,6 +33,7 @@ .cvat-player-settings-speed, .cvat-player-settings-reset-zoom, .cvat-player-settings-rotate-all, +.cvat-workspace-settings-show-text-always, .cvat-workspace-settings-show-interpolated, .cvat-workspace-settings-aam-zoom-margin, .cvat-workspace-settings-auto-save-interval { diff --git a/cvat-ui/src/components/settings-page/workspace-settings.tsx b/cvat-ui/src/components/settings-page/workspace-settings.tsx index 52ba4a50..0ee068ae 100644 --- a/cvat-ui/src/components/settings-page/workspace-settings.tsx +++ b/cvat-ui/src/components/settings-page/workspace-settings.tsx @@ -16,10 +16,12 @@ interface Props { autoSaveInterval: number; aamZoomMargin: number; showAllInterpolationTracks: boolean; + showObjectsTextAlways: boolean; onSwitchAutoSave(enabled: boolean): void; onChangeAutoSaveInterval(interval: number): void; onChangeAAMZoomMargin(margin: number): void; onSwitchShowingInterpolatedTracks(enabled: boolean): void; + onSwitchShowingObjectsTextAlways(enabled: boolean): void; } export default function WorkspaceSettingsComponent(props: Props): JSX.Element { @@ -28,10 +30,12 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element { autoSaveInterval, aamZoomMargin, showAllInterpolationTracks, + showObjectsTextAlways, onSwitchAutoSave, onChangeAutoSaveInterval, onChangeAAMZoomMargin, onSwitchShowingInterpolatedTracks, + onSwitchShowingObjectsTextAlways, } = props; const minAutoSaveInterval = 5; @@ -93,6 +97,22 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element { Show hidden interpolated objects in the side panel + + + { + onSwitchShowingObjectsTextAlways(event.target.checked); + }} + > + Always show object details + + + + Show text for an object on the canvas not only when the object is activated + + Attribute annotation mode (AAM) zoom margin diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/canvas-wrapper.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/canvas-wrapper.tsx index e5b74d9b..fc4d62e5 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/canvas-wrapper.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/canvas-wrapper.tsx @@ -73,6 +73,7 @@ interface StateToProps { saturationLevel: number; resetZoom: boolean; aamZoomMargin: number; + showObjectsTextAlways: boolean; workspace: Workspace; minZLayer: number; maxZLayer: number; @@ -163,6 +164,7 @@ function mapStateToProps(state: CombinedState): StateToProps { }, workspace: { aamZoomMargin, + showObjectsTextAlways, }, shapes: { opacity, @@ -203,6 +205,7 @@ function mapStateToProps(state: CombinedState): StateToProps { saturationLevel, resetZoom, aamZoomMargin, + showObjectsTextAlways, curZLayer, minZLayer, maxZLayer, diff --git a/cvat-ui/src/containers/settings-page/workspace-settings.tsx b/cvat-ui/src/containers/settings-page/workspace-settings.tsx index 4ab527b3..db744564 100644 --- a/cvat-ui/src/containers/settings-page/workspace-settings.tsx +++ b/cvat-ui/src/containers/settings-page/workspace-settings.tsx @@ -10,11 +10,10 @@ import { changeAutoSaveInterval, changeAAMZoomMargin, switchShowingInterpolatedTracks, + switchShowingObjectsTextAlways, } from 'actions/settings-actions'; -import { - CombinedState, -} from 'reducers/interfaces'; +import { CombinedState } from 'reducers/interfaces'; import WorkspaceSettingsComponent from 'components/settings-page/workspace-settings'; @@ -23,6 +22,7 @@ interface StateToProps { autoSaveInterval: number; aamZoomMargin: number; showAllInterpolationTracks: boolean; + showObjectsTextAlways: boolean; } interface DispatchToProps { @@ -30,6 +30,7 @@ interface DispatchToProps { onChangeAutoSaveInterval(interval: number): void; onChangeAAMZoomMargin(margin: number): void; onSwitchShowingInterpolatedTracks(enabled: boolean): void; + onSwitchShowingObjectsTextAlways(enabled: boolean): void; } function mapStateToProps(state: CombinedState): StateToProps { @@ -39,6 +40,7 @@ function mapStateToProps(state: CombinedState): StateToProps { autoSaveInterval, aamZoomMargin, showAllInterpolationTracks, + showObjectsTextAlways, } = workspace; return { @@ -46,6 +48,7 @@ function mapStateToProps(state: CombinedState): StateToProps { autoSaveInterval, aamZoomMargin, showAllInterpolationTracks, + showObjectsTextAlways, }; } @@ -63,6 +66,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { onSwitchShowingInterpolatedTracks(enabled: boolean): void { dispatch(switchShowingInterpolatedTracks(enabled)); }, + onSwitchShowingObjectsTextAlways(enabled: boolean): void { + dispatch(switchShowingObjectsTextAlways(enabled)); + }, }; } diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts index c99be219..2ab26f7c 100644 --- a/cvat-ui/src/reducers/interfaces.ts +++ b/cvat-ui/src/reducers/interfaces.ts @@ -426,6 +426,7 @@ export interface WorkspaceSettingsState { autoSave: boolean; autoSaveInterval: number; // in ms aamZoomMargin: number; + showObjectsTextAlways: boolean; showAllInterpolationTracks: boolean; } diff --git a/cvat-ui/src/reducers/settings-reducer.ts b/cvat-ui/src/reducers/settings-reducer.ts index 0a53aca4..632ed5b9 100644 --- a/cvat-ui/src/reducers/settings-reducer.ts +++ b/cvat-ui/src/reducers/settings-reducer.ts @@ -27,6 +27,7 @@ const defaultState: SettingsState = { autoSave: false, autoSaveInterval: 15 * 60 * 1000, aamZoomMargin: 100, + showObjectsTextAlways: false, showAllInterpolationTracks: false, }, 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: { const { job } = action.payload;