UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

142 lines 5.24 kB
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