src_tools_labelFactory.js
// external
import Konva from 'konva';
// doc imports
/* eslint-disable no-unused-vars */
import {Point2D} from '../math/point';
import {Style} from '../gui/style';
import {Annotation} from '../image/annotation';
/* eslint-enable no-unused-vars */
/**
* Label factory to create and update shape label.
*/
export class LabelFactory {
/**
* Default position getter.
*
* @type {Function}
*/
#defaultPositionGetter;
/**
* @param {Function} positionGetter Default position getter.
*/
constructor(positionGetter) {
this.#defaultPositionGetter = positionGetter;
}
/**
* Get the annotation label position.
*
* @param {Annotation} annotation The annotation.
* @returns {Point2D} The position.
*/
getPosition(annotation) {
let position = annotation.labelPosition;
if (typeof position === 'undefined') {
position = this.#defaultPositionGetter(annotation);
}
return position;
}
/**
* Creates the konva label.
*
* @param {Annotation} annotation The associated annotation.
* @param {Style} style The drawing style.
* @returns {Konva.Label} The Konva label.
*/
create(annotation, style) {
// konva text
const ktext = new Konva.Text({
fontSize: style.getFontSize(),
fontFamily: style.getFontFamily(),
fill: annotation.colour,
padding: style.getTextPadding(),
shadowColor: style.getShadowLineColour(),
shadowOffset: style.getShadowOffset(),
name: 'text'
});
const labelText = annotation.getText();
ktext.setText(labelText);
// times 2 so that the font size 10 looks like a 10...
// (same logic as in the DrawController::updateLabelScale)
const zoomScale = style.applyZoomScale(1);
const labelScale = {
x: 2 * zoomScale.x,
y: 2 * zoomScale.y
};
// konva label
const labelPosition = this.getPosition(annotation);
const klabel = new Konva.Label({
x: labelPosition.getX(),
y: labelPosition.getY(),
scale: labelScale,
visible: labelText.length !== 0,
name: 'label'
});
klabel.add(ktext);
klabel.add(new Konva.Tag({
fill: annotation.colour,
opacity: style.getTagOpacity()
}));
return klabel;
}
/**
* Update the shape label position.
*
* @param {Annotation} annotation The associated annotation.
* @param {Konva.Group} group The shape group.
*/
updatePosition(annotation, group) {
// associated label
const klabel = group.getChildren(function (node) {
return node.name() === 'label';
})[0];
if (!(klabel instanceof Konva.Label)) {
return;
}
// update position
const labelPosition = this.getPosition(annotation);
klabel.position({
x: labelPosition.getX(),
y: labelPosition.getY()
});
}
/**
* Get the anchors positions for the label.
*
* @param {Konva.Label} label The label.
* @returns {Point2D[]} The connectors positions.
*/
getLabelAnchorsPosition(label) {
const lx = label.x();
const ly = label.y();
const dx = label.width() * label.scale().x;
const dy = label.height() * label.scale().y;
return [
new Point2D(lx + dx / 2, ly),
new Point2D(lx, ly + dy / 2),
new Point2D(lx + dx / 2, ly + dy),
new Point2D(lx + dx, ly + dy / 2),
];
}
/**
* Get the two closest points of two points lists.
*
* @param {Point2D[]} points1 The first point list.
* @param {Point2D[]} points2 The second point list.
* @returns {Point2D[]} The closests points.
*/
getClosestPoints(points1, points2) {
let minDist = points1[0].getDistance(points2[0]);
let p1 = points1[0];
let p2 = points2[0];
for (const point1 of points1) {
for (const point2 of points2) {
const dist = point1.getDistance(point2);
if (dist < minDist) {
minDist = dist;
p1 = point1;
p2 = point2;
}
}
}
return [p1, p2];
}
/**
* Get the connector between this label and its shape.
*
* @param {Point2D[]} connectorsPos The shape connectors positions.
* @param {Konva.Label} label The label.
* @param {Style} style The drawing style.
* @returns {Konva.Line} The connector.
*/
getConnector(connectorsPos, label, style) {
const labelAnchorsPos = this.getLabelAnchorsPosition(label);
const anchorPoints = this.getClosestPoints(
connectorsPos, labelAnchorsPos);
return new Konva.Line({
points: [
anchorPoints[0].getX(),
anchorPoints[0].getY(),
anchorPoints[1].getX(),
anchorPoints[1].getY()
],
stroke: label.getText().fill(),
strokeWidth: style.getStrokeWidth(),
strokeScaleEnabled: false,
visible: label.visible(),
dash: [10, 7],
name: 'connector'
});
}
/**
* Update the connector between a label and its shape.
*
* @param {Konva.Group} group The associated shape group.
* @param {Point2D[]} connectorsPos The shape connectors positions.
*/
updateConnector(group, connectorsPos) {
// associated label
const klabel = group.getChildren(function (node) {
return node.name() === 'label';
})[0];
if (!(klabel instanceof Konva.Label)) {
return;
}
const labelAnchorsPos = this.getLabelAnchorsPosition(klabel);
const anchors = this.getClosestPoints(connectorsPos, labelAnchorsPos);
const kconnect = group.getChildren(function (node) {
return node.name() === 'connector';
})[0];
if (!(kconnect instanceof Konva.Line)) {
return;
}
kconnect.points([
anchors[0].getX(),
anchors[0].getY(),
anchors[1].getX(),
anchors[1].getY()
]);
}
/**
* Update the shape label.
*
* @param {Annotation} annotation The associated annotation.
* @param {Konva.Group} group The shape group.
*/
updateContent(annotation, group) {
// associated label
const klabel = group.getChildren(function (node) {
return node.name() === 'label';
})[0];
if (!(klabel instanceof Konva.Label)) {
return;
}
// update text
const text = annotation.getText();
const ktext = klabel.getText();
ktext.setText(text);
// hide if visible and empty
if (klabel.visible()) {
klabel.visible(text.length !== 0);
}
}
} // LabelFactory