Added options to switch text font size and position (#3972)

main
Boris Sekachev 4 years ago committed by GitHub
parent c79a4c1584
commit ab351c2aa0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Google Cloud Storage support in UI (<https://github.com/openvinotoolkit/cvat/pull/3919>) - Google Cloud Storage support in UI (<https://github.com/openvinotoolkit/cvat/pull/3919>)
- Add project tasks paginations (<https://github.com/openvinotoolkit/cvat/pull/3910>) - Add project tasks paginations (<https://github.com/openvinotoolkit/cvat/pull/3910>)
- Add remove issue button (<https://github.com/openvinotoolkit/cvat/pull/3952>) - Add remove issue button (<https://github.com/openvinotoolkit/cvat/pull/3952>)
- Options to change font size & position of text labels on the canvas (<https://github.com/openvinotoolkit/cvat/pull/3972>)
### Changed ### Changed
- TDB - TDB

@ -1,21 +1,35 @@
{ {
"name": "cvat-canvas", "name": "cvat-canvas",
"version": "2.10.0", "version": "2.10.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "cvat-canvas", "name": "cvat-canvas",
"version": "2.10.0", "version": "2.10.1",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/polylabel": "^1.0.5",
"polylabel": "^1.1.0",
"svg.draggable.js": "2.2.2", "svg.draggable.js": "2.2.2",
"svg.draw.js": "^2.0.4", "svg.draw.js": "^2.0.4",
"svg.js": "2.7.1", "svg.js": "2.7.1",
"svg.resize.js": "1.4.3", "svg.resize.js": "1.4.3",
"svg.select.js": "3.0.1" "svg.select.js": "3.0.1"
}, }
"devDependencies": {} },
"node_modules/@types/polylabel": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/polylabel/-/polylabel-1.0.5.tgz",
"integrity": "sha512-gnaNmo1OJiYNBFAZMZdqLZ3hKx2ee4ksAzqhKWBxuQ61PmhINHMcvIqsGmyCD1WFKCkwRt9NFhMSmKE6AgYY+w=="
},
"node_modules/polylabel": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/polylabel/-/polylabel-1.1.0.tgz",
"integrity": "sha512-bxaGcA40sL3d6M4hH72Z4NdLqxpXRsCFk8AITYg6x1rn1Ei3izf00UMLklerBZTO49aPA3CYrIwVulx2Bce2pA==",
"dependencies": {
"tinyqueue": "^2.0.3"
}
}, },
"node_modules/svg.draggable.js": { "node_modules/svg.draggable.js": {
"version": "2.2.2", "version": "2.2.2",
@ -77,9 +91,27 @@
"engines": { "engines": {
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
},
"node_modules/tinyqueue": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz",
"integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA=="
} }
}, },
"dependencies": { "dependencies": {
"@types/polylabel": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/polylabel/-/polylabel-1.0.5.tgz",
"integrity": "sha512-gnaNmo1OJiYNBFAZMZdqLZ3hKx2ee4ksAzqhKWBxuQ61PmhINHMcvIqsGmyCD1WFKCkwRt9NFhMSmKE6AgYY+w=="
},
"polylabel": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/polylabel/-/polylabel-1.1.0.tgz",
"integrity": "sha512-bxaGcA40sL3d6M4hH72Z4NdLqxpXRsCFk8AITYg6x1rn1Ei3izf00UMLklerBZTO49aPA3CYrIwVulx2Bce2pA==",
"requires": {
"tinyqueue": "^2.0.3"
}
},
"svg.draggable.js": { "svg.draggable.js": {
"version": "2.2.2", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz",
@ -127,6 +159,11 @@
"requires": { "requires": {
"svg.js": "^2.6.5" "svg.js": "^2.6.5"
} }
},
"tinyqueue": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz",
"integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA=="
} }
} }
} }

@ -1,6 +1,6 @@
{ {
"name": "cvat-canvas", "name": "cvat-canvas",
"version": "2.10.0", "version": "2.10.1",
"description": "Part of Computer Vision Annotation Tool which presents its canvas library", "description": "Part of Computer Vision Annotation Tool which presents its canvas library",
"main": "src/canvas.ts", "main": "src/canvas.ts",
"scripts": { "scripts": {
@ -15,8 +15,9 @@
"not IE 11", "not IE 11",
"> 2%" "> 2%"
], ],
"devDependencies": {},
"dependencies": { "dependencies": {
"@types/polylabel": "^1.0.5",
"polylabel": "^1.1.0",
"svg.draggable.js": "2.2.2", "svg.draggable.js": "2.2.2",
"svg.draw.js": "^2.0.4", "svg.draw.js": "^2.0.4",
"svg.js": "2.7.1", "svg.js": "2.7.1",

@ -37,7 +37,6 @@ polyline.cvat_shape_drawing_opacity {
.cvat_canvas_text { .cvat_canvas_text {
font-weight: bold; font-weight: bold;
font-size: 1.2em;
fill: white; fill: white;
cursor: default; cursor: default;
font-family: Calibri, Candara, Segoe, 'Segoe UI', Optima, Arial, sans-serif; font-family: Calibri, Candara, Segoe, 'Segoe UI', Optima, Arial, sans-serif;
@ -47,7 +46,6 @@ polyline.cvat_shape_drawing_opacity {
} }
.cvat_canvas_text_description { .cvat_canvas_text_description {
font-size: 14px;
fill: yellow; fill: yellow;
font-style: oblique 40deg; font-style: oblique 40deg;
} }

@ -55,6 +55,8 @@ export interface Configuration {
smoothImage?: boolean; smoothImage?: boolean;
autoborders?: boolean; autoborders?: boolean;
displayAllText?: boolean; displayAllText?: boolean;
textFontSize?: number;
textPosition?: 'auto' | 'center';
undefinedAttrValue?: string; undefinedAttrValue?: string;
showProjections?: boolean; showProjections?: boolean;
forceDisableEditing?: boolean; forceDisableEditing?: boolean;
@ -647,6 +649,14 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
this.data.configuration.displayAllText = configuration.displayAllText; this.data.configuration.displayAllText = configuration.displayAllText;
} }
if (typeof configuration.textFontSize === 'number') {
this.data.configuration.textFontSize = configuration.textFontSize;
}
if (['auto', 'center'].includes(configuration.textPosition)) {
this.data.configuration.textPosition = configuration.textPosition;
}
if (typeof configuration.showProjections === 'boolean') { if (typeof configuration.showProjections === 'boolean') {
this.data.configuration.showProjections = configuration.showProjections; this.data.configuration.showProjections = configuration.showProjections;
} }

@ -2,6 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import polylabel from 'polylabel';
import * as SVG from 'svg.js'; import * as SVG from 'svg.js';
import 'svg.draggable.js'; import 'svg.draggable.js';
@ -683,11 +684,13 @@ export class CanvasViewImpl implements CanvasView, Listener {
for (const state of deleted) { for (const state of deleted) {
if (state.clientID in this.svgTexts) { if (state.clientID in this.svgTexts) {
this.svgTexts[state.clientID].remove(); this.svgTexts[state.clientID].remove();
delete this.svgTexts[state.clientID];
} }
this.svgShapes[state.clientID].off('click.canvas'); this.svgShapes[state.clientID].off('click.canvas');
this.svgShapes[state.clientID].remove(); this.svgShapes[state.clientID].remove();
delete this.drawnStates[state.clientID]; delete this.drawnStates[state.clientID];
delete this.svgShapes[state.clientID];
} }
this.addObjects(created); this.addObjects(created);
@ -1175,7 +1178,6 @@ export class CanvasViewImpl implements CanvasView, Listener {
for (const i in this.drawnStates) { for (const i in this.drawnStates) {
if (!(i in this.svgTexts)) { if (!(i in this.svgTexts)) {
this.svgTexts[i] = this.addText(this.drawnStates[i]); this.svgTexts[i] = this.addText(this.drawnStates[i]);
this.updateTextPosition(this.svgTexts[i], this.svgShapes[i]);
} }
} }
} else if (configuration.displayAllText === false && this.configuration.displayAllText) { } else if (configuration.displayAllText === false && this.configuration.displayAllText) {
@ -1187,15 +1189,25 @@ export class CanvasViewImpl implements CanvasView, Listener {
} }
} }
if ('smoothImage' in configuration) { const updateTextPosition = configuration.displayAllText !== this.configuration.displayAllText ||
if (configuration.smoothImage) { configuration.textFontSize !== this.configuration.textFontSize ||
this.background.classList.remove('cvat_canvas_pixelized'); configuration.textPosition !== this.configuration.textPosition;
} else {
this.background.classList.add('cvat_canvas_pixelized'); if (configuration.smoothImage === true) {
} this.background.classList.remove('cvat_canvas_pixelized');
} else if (configuration.smoothImage === false) {
this.background.classList.add('cvat_canvas_pixelized');
} }
this.configuration = configuration; this.configuration = configuration;
if (updateTextPosition) {
for (const i in this.drawnStates) {
if (i in this.svgTexts) {
this.updateTextPosition(this.svgTexts[i], this.svgShapes[i]);
}
}
}
this.activate(activeElement); this.activate(activeElement);
this.editHandler.configurate(this.configuration); this.editHandler.configurate(this.configuration);
this.drawHandler.configurate(this.configuration); this.drawHandler.configurate(this.configuration);
@ -2059,45 +2071,82 @@ export class CanvasViewImpl implements CanvasView, Listener {
// Update text position after corresponding box has been moved, resized, etc. // Update text position after corresponding box has been moved, resized, etc.
private updateTextPosition(text: SVG.Text, shape: SVG.Shape): void { private updateTextPosition(text: SVG.Text, shape: SVG.Shape): void {
if (text.node.style.display === 'none') return; // wrong transformation matrix if (text.node.style.display === 'none') return; // wrong transformation matrix
const { rotation } = shape.transform(); const textFontSize = this.configuration.textFontSize || consts.DEFAULT_SHAPE_TEXT_SIZE;
let box = (shape.node as any).getBBox(); const textPosition = this.configuration.textPosition || 'auto';
// Translate the whole box to the client coordinate system
const [x1, y1, x2, y2]: number[] = translateFromSVG(this.content, [
box.x,
box.y,
box.x + box.width,
box.y + box.height,
]);
box = { text.untransform();
x: Math.min(x1, x2), text.style({ 'font-size': textFontSize });
y: Math.min(y1, y2), const { rotation } = shape.transform();
width: Math.max(x1, x2) - Math.min(x1, x2),
height: Math.max(y1, y2) - Math.min(y1, y2),
};
// Find the best place for a text // Find the best place for a text
let [clientX, clientY]: number[] = [box.x + box.width, box.y]; let [clientX, clientY, clientCX, clientCY]: number[] = [0, 0, 0, 0];
if ( if (textPosition === 'center') {
clientX + ((text.node as any) as SVGTextElement) let cx = 0;
.getBBox().width + consts.TEXT_MARGIN > this.canvas.offsetWidth let cy = 0;
) { if (shape.type === 'rect') {
[clientX, clientY] = [box.x, box.y]; // for rectangle finding a center is simple
cx = +shape.attr('x') + +shape.attr('width') / 2;
cy = +shape.attr('y') + +shape.attr('height') / 2;
} else {
// for polyshapes we use special algorithm
const points = parsePoints(pointsToNumberArray(shape.attr('points')));
[cx, cy] = polylabel([points.map((point) => [point.x, point.y])]);
}
[clientX, clientY] = translateFromSVG(this.content, [cx, cy]);
// center is exactly clientX, clientY
clientCX = clientX;
clientCY = clientY;
} else {
let box = (shape.node as any).getBBox();
// Translate the whole box to the client coordinate system
const [x1, y1, x2, y2]: number[] = translateFromSVG(this.content, [
box.x,
box.y,
box.x + box.width,
box.y + box.height,
]);
clientCX = x1 + (x2 - x1) / 2;
clientCY = y1 + (y2 - y1) / 2;
box = {
x: Math.min(x1, x2),
y: Math.min(y1, y2),
width: Math.max(x1, x2) - Math.min(x1, x2),
height: Math.max(y1, y2) - Math.min(y1, y2),
};
// first try to put to the top right corner
[clientX, clientY] = [box.x + box.width, box.y];
if (
clientX + ((text.node as any) as SVGTextElement)
.getBBox().width + consts.TEXT_MARGIN > this.canvas.offsetWidth
) {
// if out of visible area, try to put text to top left corner
[clientX, clientY] = [box.x, box.y];
}
} }
// Translate back to text SVG // Translate found coordinates to text SVG
const [x, y, cx, cy]: number[] = translateToSVG(this.text, [ const [x, y, rotX, rotY]: number[] = translateToSVG(this.text, [
clientX + consts.TEXT_MARGIN, clientX + consts.TEXT_MARGIN,
clientY + consts.TEXT_MARGIN, clientY + consts.TEXT_MARGIN,
x1 + (x2 - x1) / 2, clientCX,
y1 + (y2 - y1) / 2, clientCY,
]); ]);
const textBBox = ((text.node as any) as SVGTextElement).getBBox();
// Finally draw a text // Finally draw a text
text.move(x, y); if (textPosition === 'center') {
text.move(x - textBBox.width / 2, y - textBBox.height / 2);
} else {
text.move(x, y);
}
if (rotation) { if (rotation) {
text.rotate(rotation, cx, cy); text.rotate(rotation, rotX, rotY);
} }
for (const tspan of (text.lines() as any).members) { for (const tspan of (text.lines() as any).members) {
@ -2107,6 +2156,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
private addText(state: any): SVG.Text { private addText(state: any): SVG.Text {
const { undefinedAttrValue } = this.configuration; const { undefinedAttrValue } = this.configuration;
const textFontSize = this.configuration.textFontSize || 12;
const { const {
label, clientID, attributes, source, descriptions, label, clientID, attributes, source, descriptions,
} = state; } = state;
@ -2117,7 +2167,9 @@ export class CanvasViewImpl implements CanvasView, Listener {
return this.adoptedText return this.adoptedText
.text((block): void => { .text((block): void => {
block.tspan(`${label.name} ${clientID} (${source})`).style('text-transform', 'uppercase'); block.tspan(`${label.name} ${clientID} (${source})`).style({
'text-transform': 'uppercase',
});
for (const desc of descriptions) { for (const desc of descriptions) {
block block
.tspan(`${desc}`) .tspan(`${desc}`)
@ -2140,6 +2192,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
} }
}) })
.move(0, 0) .move(0, 0)
.style({ 'font-size': textFontSize })
.addClass('cvat_canvas_text'); .addClass('cvat_canvas_text');
} }

@ -19,6 +19,7 @@ const ARROW_PATH = 'M13.162 6.284L.682.524a.483.483 0 0 0-.574.134.477.477 0 ' +
const BASE_PATTERN_SIZE = 5; const BASE_PATTERN_SIZE = 5;
const SNAP_TO_ANGLE_RESIZE_DEFAULT = 0.1; const SNAP_TO_ANGLE_RESIZE_DEFAULT = 0.1;
const SNAP_TO_ANGLE_RESIZE_SHIFT = 15; const SNAP_TO_ANGLE_RESIZE_SHIFT = 15;
const DEFAULT_SHAPE_TEXT_SIZE = 12;
export default { export default {
BASE_STROKE_WIDTH, BASE_STROKE_WIDTH,
@ -37,4 +38,5 @@ export default {
BASE_PATTERN_SIZE, BASE_PATTERN_SIZE,
SNAP_TO_ANGLE_RESIZE_DEFAULT, SNAP_TO_ANGLE_RESIZE_DEFAULT,
SNAP_TO_ANGLE_RESIZE_SHIFT, SNAP_TO_ANGLE_RESIZE_SHIFT,
DEFAULT_SHAPE_TEXT_SIZE,
}; };

@ -23,6 +23,8 @@ export enum SettingsActionTypes {
CHANGE_FRAME_SPEED = 'CHANGE_FRAME_SPEED', CHANGE_FRAME_SPEED = 'CHANGE_FRAME_SPEED',
SWITCH_RESET_ZOOM = 'SWITCH_RESET_ZOOM', SWITCH_RESET_ZOOM = 'SWITCH_RESET_ZOOM',
SWITCH_SMOOTH_IMAGE = 'SWITCH_SMOOTH_IMAGE', SWITCH_SMOOTH_IMAGE = 'SWITCH_SMOOTH_IMAGE',
SWITCH_TEXT_FONT_SIZE = 'SWITCH_TEXT_FONT_SIZE',
SWITCH_TEXT_POSITION = 'SWITCH_TEXT_POSITION',
CHANGE_BRIGHTNESS_LEVEL = 'CHANGE_BRIGHTNESS_LEVEL', CHANGE_BRIGHTNESS_LEVEL = 'CHANGE_BRIGHTNESS_LEVEL',
CHANGE_CONTRAST_LEVEL = 'CHANGE_CONTRAST_LEVEL', CHANGE_CONTRAST_LEVEL = 'CHANGE_CONTRAST_LEVEL',
CHANGE_SATURATION_LEVEL = 'CHANGE_SATURATION_LEVEL', CHANGE_SATURATION_LEVEL = 'CHANGE_SATURATION_LEVEL',
@ -176,6 +178,24 @@ export function switchSmoothImage(enabled: boolean): AnyAction {
}; };
} }
export function switchTextFontSize(fontSize: number): AnyAction {
return {
type: SettingsActionTypes.SWITCH_TEXT_FONT_SIZE,
payload: {
fontSize,
},
};
}
export function switchTextPosition(position: 'auto' | 'center'): AnyAction {
return {
type: SettingsActionTypes.SWITCH_TEXT_POSITION,
payload: {
position,
},
};
}
export function changeBrightnessLevel(level: number): AnyAction { export function changeBrightnessLevel(level: number): AnyAction {
return { return {
type: SettingsActionTypes.CHANGE_BRIGHTNESS_LEVEL, type: SettingsActionTypes.CHANGE_BRIGHTNESS_LEVEL,

@ -60,6 +60,8 @@ interface Props {
smoothImage: boolean; smoothImage: boolean;
aamZoomMargin: number; aamZoomMargin: number;
showObjectsTextAlways: boolean; showObjectsTextAlways: boolean;
textFontSize: number;
textPosition: 'auto' | 'center';
showAllInterpolationTracks: boolean; showAllInterpolationTracks: boolean;
workspace: Workspace; workspace: Workspace;
automaticBordering: boolean; automaticBordering: boolean;
@ -107,6 +109,8 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
showProjections, showProjections,
selectedOpacity, selectedOpacity,
smoothImage, smoothImage,
textFontSize,
textPosition,
} = this.props; } = this.props;
const { canvasInstance } = this.props as { canvasInstance: Canvas }; const { canvasInstance } = this.props as { canvasInstance: Canvas };
@ -124,6 +128,8 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
intelligentPolygonCrop, intelligentPolygonCrop,
showProjections, showProjections,
creationOpacity: selectedOpacity, creationOpacity: selectedOpacity,
textFontSize,
textPosition,
}); });
this.initialSetup(); this.initialSetup();
@ -158,6 +164,8 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
workspace, workspace,
frameFetching, frameFetching,
showObjectsTextAlways, showObjectsTextAlways,
textFontSize,
textPosition,
showAllInterpolationTracks, showAllInterpolationTracks,
automaticBordering, automaticBordering,
intelligentPolygonCrop, intelligentPolygonCrop,
@ -172,7 +180,9 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
prevProps.showProjections !== showProjections || prevProps.showProjections !== showProjections ||
prevProps.intelligentPolygonCrop !== intelligentPolygonCrop || prevProps.intelligentPolygonCrop !== intelligentPolygonCrop ||
prevProps.selectedOpacity !== selectedOpacity || prevProps.selectedOpacity !== selectedOpacity ||
prevProps.smoothImage !== smoothImage prevProps.smoothImage !== smoothImage ||
prevProps.textFontSize !== textFontSize ||
prevProps.textPosition !== textPosition
) { ) {
canvasInstance.configure({ canvasInstance.configure({
undefinedAttrValue: consts.UNDEFINED_ATTRIBUTE_VALUE, undefinedAttrValue: consts.UNDEFINED_ATTRIBUTE_VALUE,
@ -182,6 +192,8 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
intelligentPolygonCrop, intelligentPolygonCrop,
creationOpacity: selectedOpacity, creationOpacity: selectedOpacity,
smoothImage, smoothImage,
textFontSize,
textPosition,
}); });
} }

@ -29,11 +29,12 @@
.cvat-workspace-settings-show-text-always, .cvat-workspace-settings-show-text-always,
.cvat-workspace-settings-show-interpolated, .cvat-workspace-settings-show-interpolated,
.cvat-workspace-settings-approx-poly-threshold, .cvat-workspace-settings-approx-poly-threshold,
.cvat-workspace-settings-aam-zoom-margin { .cvat-workspace-settings-aam-zoom-margin,
margin-bottom: 25px; .cvat-workspace-settings-text-settings {
margin-bottom: $grid-unit-size * 3;
> div:first-child { > div:first-child {
margin-bottom: 10px; margin-bottom: $grid-unit-size;
} }
} }
@ -48,7 +49,7 @@
.cvat-player-settings-canvas-background, .cvat-player-settings-canvas-background,
.cvat-workspace-settings-aam-zoom-margin, .cvat-workspace-settings-aam-zoom-margin,
.cvat-workspace-settings-auto-save-interval { .cvat-workspace-settings-auto-save-interval {
margin-bottom: 25px; margin-bottom: $grid-unit-size * 3;
} }
.cvat-player-settings-step, .cvat-player-settings-step,

@ -15,6 +15,7 @@ import {
marks, marks,
} from 'components/annotation-page/standard-workspace/controls-side-bar/approximation-accuracy'; } from 'components/annotation-page/standard-workspace/controls-side-bar/approximation-accuracy';
import { clamp } from 'utils/math'; import { clamp } from 'utils/math';
import { Select } from 'antd';
interface Props { interface Props {
autoSave: boolean; autoSave: boolean;
@ -25,6 +26,8 @@ interface Props {
automaticBordering: boolean; automaticBordering: boolean;
intelligentPolygonCrop: boolean; intelligentPolygonCrop: boolean;
defaultApproxPolyAccuracy: number; defaultApproxPolyAccuracy: number;
textFontSize: number;
textPosition: 'center' | 'auto';
onSwitchAutoSave(enabled: boolean): void; onSwitchAutoSave(enabled: boolean): void;
onChangeAutoSaveInterval(interval: number): void; onChangeAutoSaveInterval(interval: number): void;
onChangeAAMZoomMargin(margin: number): void; onChangeAAMZoomMargin(margin: number): void;
@ -33,6 +36,8 @@ interface Props {
onSwitchShowingObjectsTextAlways(enabled: boolean): void; onSwitchShowingObjectsTextAlways(enabled: boolean): void;
onSwitchAutomaticBordering(enabled: boolean): void; onSwitchAutomaticBordering(enabled: boolean): void;
onSwitchIntelligentPolygonCrop(enabled: boolean): void; onSwitchIntelligentPolygonCrop(enabled: boolean): void;
onChangeTextFontSize(fontSize: number): void;
onChangeTextPosition(position: 'auto' | 'center'): void;
} }
function WorkspaceSettingsComponent(props: Props): JSX.Element { function WorkspaceSettingsComponent(props: Props): JSX.Element {
@ -45,6 +50,8 @@ function WorkspaceSettingsComponent(props: Props): JSX.Element {
automaticBordering, automaticBordering,
intelligentPolygonCrop, intelligentPolygonCrop,
defaultApproxPolyAccuracy, defaultApproxPolyAccuracy,
textFontSize,
textPosition,
onSwitchAutoSave, onSwitchAutoSave,
onChangeAutoSaveInterval, onChangeAutoSaveInterval,
onChangeAAMZoomMargin, onChangeAAMZoomMargin,
@ -53,6 +60,8 @@ function WorkspaceSettingsComponent(props: Props): JSX.Element {
onSwitchAutomaticBordering, onSwitchAutomaticBordering,
onSwitchIntelligentPolygonCrop, onSwitchIntelligentPolygonCrop,
onChangeDefaultApproxPolyAccuracy, onChangeDefaultApproxPolyAccuracy,
onChangeTextFontSize,
onChangeTextPosition,
} = props; } = props;
const minAutoSaveInterval = 1; const minAutoSaveInterval = 1;
@ -128,6 +137,23 @@ function WorkspaceSettingsComponent(props: Props): JSX.Element {
</Text> </Text>
</Col> </Col>
</Row> </Row>
<Row className='cvat-workspace-settings-text-settings'>
<Col span={12}>
<Text>Position of a text</Text>
</Col>
<Col span={12}>
<Text>Font size of a text</Text>
</Col>
<Col span={12}>
<Select value={textPosition} onChange={onChangeTextPosition}>
<Select.Option value='auto'>Auto</Select.Option>
<Select.Option value='center'>Center</Select.Option>
</Select>
</Col>
<Col span={12}>
<InputNumber onChange={onChangeTextFontSize} min={8} max={20} value={textFontSize} />
</Col>
</Row>
<Row className='cvat-workspace-settings-autoborders'> <Row className='cvat-workspace-settings-autoborders'>
<Col span={24}> <Col span={24}>
<Checkbox <Checkbox

@ -83,6 +83,8 @@ interface StateToProps {
smoothImage: boolean; smoothImage: boolean;
aamZoomMargin: number; aamZoomMargin: number;
showObjectsTextAlways: boolean; showObjectsTextAlways: boolean;
textFontSize: number;
textPosition: 'auto' | 'center';
showAllInterpolationTracks: boolean; showAllInterpolationTracks: boolean;
workspace: Workspace; workspace: Workspace;
minZLayer: number; minZLayer: number;
@ -164,6 +166,8 @@ function mapStateToProps(state: CombinedState): StateToProps {
showAllInterpolationTracks, showAllInterpolationTracks,
automaticBordering, automaticBordering,
intelligentPolygonCrop, intelligentPolygonCrop,
textFontSize,
textPosition,
}, },
shapes: { shapes: {
opacity, colorBy, selectedOpacity, outlined, outlineColor, showBitmap, showProjections, opacity, colorBy, selectedOpacity, outlined, outlineColor, showBitmap, showProjections,
@ -210,6 +214,8 @@ function mapStateToProps(state: CombinedState): StateToProps {
smoothImage, smoothImage,
aamZoomMargin, aamZoomMargin,
showObjectsTextAlways, showObjectsTextAlways,
textFontSize,
textPosition,
showAllInterpolationTracks, showAllInterpolationTracks,
curZLayer, curZLayer,
minZLayer, minZLayer,

@ -14,6 +14,8 @@ import {
switchAutomaticBordering, switchAutomaticBordering,
switchIntelligentPolygonCrop, switchIntelligentPolygonCrop,
changeDefaultApproxPolyAccuracy, changeDefaultApproxPolyAccuracy,
switchTextFontSize,
switchTextPosition,
} from 'actions/settings-actions'; } from 'actions/settings-actions';
import { CombinedState } from 'reducers/interfaces'; import { CombinedState } from 'reducers/interfaces';
@ -29,6 +31,8 @@ interface StateToProps {
defaultApproxPolyAccuracy: number; defaultApproxPolyAccuracy: number;
automaticBordering: boolean; automaticBordering: boolean;
intelligentPolygonCrop: boolean; intelligentPolygonCrop: boolean;
textFontSize: number;
textPosition: 'auto' | 'center';
} }
interface DispatchToProps { interface DispatchToProps {
@ -40,6 +44,8 @@ interface DispatchToProps {
onSwitchAutomaticBordering(enabled: boolean): void; onSwitchAutomaticBordering(enabled: boolean): void;
onSwitchIntelligentPolygonCrop(enabled: boolean): void; onSwitchIntelligentPolygonCrop(enabled: boolean): void;
onChangeDefaultApproxPolyAccuracy(approxPolyAccuracy: number): void; onChangeDefaultApproxPolyAccuracy(approxPolyAccuracy: number): void;
onChangeTextFontSize(fontSize: number): void;
onChangeTextPosition(position: 'auto' | 'center'): void;
} }
function mapStateToProps(state: CombinedState): StateToProps { function mapStateToProps(state: CombinedState): StateToProps {
@ -53,6 +59,8 @@ function mapStateToProps(state: CombinedState): StateToProps {
automaticBordering, automaticBordering,
intelligentPolygonCrop, intelligentPolygonCrop,
defaultApproxPolyAccuracy, defaultApproxPolyAccuracy,
textFontSize,
textPosition,
} = workspace; } = workspace;
return { return {
@ -64,6 +72,8 @@ function mapStateToProps(state: CombinedState): StateToProps {
automaticBordering, automaticBordering,
intelligentPolygonCrop, intelligentPolygonCrop,
defaultApproxPolyAccuracy, defaultApproxPolyAccuracy,
textFontSize,
textPosition,
}; };
} }
@ -93,6 +103,12 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
onChangeDefaultApproxPolyAccuracy(threshold: number): void { onChangeDefaultApproxPolyAccuracy(threshold: number): void {
dispatch(changeDefaultApproxPolyAccuracy(threshold)); dispatch(changeDefaultApproxPolyAccuracy(threshold));
}, },
onChangeTextFontSize(fontSize: number): void {
dispatch(switchTextFontSize(fontSize));
},
onChangeTextPosition(position: 'auto' | 'center'): void {
dispatch(switchTextPosition(position));
},
}; };
} }

@ -640,6 +640,8 @@ export interface WorkspaceSettingsState {
intelligentPolygonCrop: boolean; intelligentPolygonCrop: boolean;
defaultApproxPolyAccuracy: number; defaultApproxPolyAccuracy: number;
toolsBlockerState: ToolsBlockerState; toolsBlockerState: ToolsBlockerState;
textFontSize: number;
textPosition: 'auto' | 'center';
} }
export interface ShapesSettingsState { export interface ShapesSettingsState {

@ -32,6 +32,8 @@ const defaultState: SettingsState = {
showAllInterpolationTracks: false, showAllInterpolationTracks: false,
intelligentPolygonCrop: true, intelligentPolygonCrop: true,
defaultApproxPolyAccuracy: 9, defaultApproxPolyAccuracy: 9,
textFontSize: 14,
textPosition: 'auto',
toolsBlockerState: { toolsBlockerState: {
algorithmsLocked: false, algorithmsLocked: false,
buttonVisible: false, buttonVisible: false,
@ -193,6 +195,24 @@ export default (state = defaultState, action: AnyAction): SettingsState => {
}, },
}; };
} }
case SettingsActionTypes.SWITCH_TEXT_FONT_SIZE: {
return {
...state,
workspace: {
...state.workspace,
textFontSize: action.payload.fontSize,
},
};
}
case SettingsActionTypes.SWITCH_TEXT_POSITION: {
return {
...state,
workspace: {
...state.workspace,
textPosition: action.payload.position,
},
};
}
case SettingsActionTypes.CHANGE_BRIGHTNESS_LEVEL: { case SettingsActionTypes.CHANGE_BRIGHTNESS_LEVEL: {
return { return {
...state, ...state,

Loading…
Cancel
Save