@bokeh/bokehjs
Version:
Interactive, novel data visualization
142 lines • 5.24 kB
JavaScript
import { TextAnnotation, TextAnnotationView } from "./text_annotation";
import { compute_angle, invert_angle, atan2 } from "../../core/util/math";
import { CoordinateUnits, AngleUnits, Direction } from "../../core/enums";
import { assert } from "../../core/util/assert";
import { Signal } from "../../core/signaling";
import { rotate_around } from "../../core/util/affine";
import { TextAnchor } from "../common/kinds";
import * as resolve from "../common/resolve";
import { Coordinate } from "../coordinates/coordinate";
function xy(x, y) {
return { x, y };
}
export class LabelView extends TextAnnotationView {
static __name__ = "LabelView";
get mappers() {
function mapper(units, scale, view, canvas) {
switch (units) {
case "canvas": return canvas;
case "screen": return view;
case "data": return scale;
}
}
const overlay = this.model;
const parent = this.layout ?? this.plot_view.frame;
const { x_scale, y_scale } = this.coordinates;
const { x_view, y_view } = parent.bbox;
const { x_screen, y_screen } = this.plot_view.canvas.bbox;
const xy = {
x: mapper(overlay.x_units, x_scale, x_view, x_screen),
y: mapper(overlay.y_units, y_scale, y_view, y_screen),
};
return xy;
}
get anchor() {
const { align, baseline } = this.visuals.text.values();
return resolve.text_anchor(this.model.anchor, align, baseline);
}
get angle() {
const { angle, angle_units, direction } = this.model;
return compute_angle(angle, angle_units, direction);
}
get origin() {
const { mappers } = this;
const { x, y, x_offset, y_offset } = this.model;
const compute = (dim, value, mapper) => {
return value instanceof Coordinate ? this.resolve_as_scalar(value, dim) : mapper.compute(value);
};
const sx = compute("x", x, mappers.x) + x_offset;
const sy = compute("y", y, mappers.y) - y_offset; // TODO this needs to be unified with the rest of bokehjs
return { sx, sy };
}
interactive_hit(sx, sy) {
if (!this.model.visible || !this.model.editable) {
return false;
}
return this._hit_test(sx, sy) == "area";
}
_hit_test(cx, cy) {
const { sx, sy, anchor, angle, width, height } = this._rect;
const { x, y } = rotate_around(xy(cx, cy), xy(sx, sy), -angle);
const left = sx - anchor.x * width;
const top = sy - anchor.y * height;
const right = left + width;
const bottom = top + height;
if (left <= x && x <= right && top <= y && y <= bottom) {
return "area";
}
else {
return null;
}
}
_can_hit(_target) {
return true;
}
_pan_state = null;
on_pan_start(ev) {
if (this.model.visible && this.model.editable) {
const { sx, sy } = ev;
const target = this._hit_test(sx, sy);
if (target != null && this._can_hit(target)) {
this._pan_state = {
angle: this.angle,
base: { sx, sy },
target,
action: "rotate",
};
this.model.pan.emit(["pan:start", ev.modifiers]);
return true;
}
}
return false;
}
on_pan(ev) {
assert(this._pan_state != null);
const { dx, dy } = ev;
const { angle, base } = this._pan_state;
const { origin } = this;
const angle0 = atan2([origin.sx, origin.sy], [base.sx, base.sy]);
const angle1 = atan2([origin.sx, origin.sy], [base.sx + dx, base.sy + dy]);
const da = angle1 - angle0;
const na = angle + da;
const nna = na % (2 * Math.PI);
const { angle_units, direction } = this.model;
this.model.angle = invert_angle(nna, angle_units, direction);
this.model.pan.emit(["pan", ev.modifiers]);
}
on_pan_end(ev) {
this._pan_state = null;
this.model.pan.emit(["pan:end", ev.modifiers]);
}
cursor(sx, sy) {
const target = this._pan_state?.target ?? this._hit_test(sx, sy);
if (target == null || !this._can_hit(target)) {
return null;
}
return "var(--bokeh-cursor-rotate)";
}
}
export class Label extends TextAnnotation {
static __name__ = "Label";
constructor(attrs) {
super(attrs);
}
static {
this.prototype.default_view = LabelView;
this.define(({ Bool, Float, Angle, Or, Ref }) => ({
anchor: [TextAnchor, "auto"],
x: [Or(Float, Ref(Coordinate))],
y: [Or(Float, Ref(Coordinate))],
x_units: [CoordinateUnits, "data"],
y_units: [CoordinateUnits, "data"],
x_offset: [Float, 0],
y_offset: [Float, 0],
angle: [Angle, 0],
angle_units: [AngleUnits, "rad"],
direction: [Direction, "anticlock"],
editable: [Bool, false],
}));
}
pan = new Signal(this, "pan");
}
//# sourceMappingURL=label.js.map