@bokeh/bokehjs
Version:
Interactive, novel data visualization
124 lines • 5.01 kB
JavaScript
import { XYGlyph, XYGlyphView } from "./xy_glyph";
import { inherit } from "./glyph";
import { generic_area_vector_legend } from "./utils";
import { LineVector, FillVector, HatchVector } from "../../core/property_mixins";
import { to_screen } from "../../core/types";
import { Direction } from "../../core/enums";
import * as p from "../../core/properties";
import { angle_between } from "../../core/util/math";
import { Selection } from "../selections/selection";
import { max } from "../../core/util/arrayable";
export class WedgeView extends XYGlyphView {
static __name__ = "WedgeView";
async load_glglyph() {
const { WedgeGL } = await import("./webgl/wedge");
return WedgeGL;
}
_index_data(index) {
const { x, y, radius, data_size } = this;
for (let i = 0; i < data_size; i++) {
const x_i = x[i];
const y_i = y[i];
const r_i = radius.get(i);
index.add_rect(x_i - r_i, y_i - r_i, x_i + r_i, y_i + r_i);
}
}
_map_data() {
this._define_or_inherit_attr("sradius", () => {
if (this.model.properties.radius.units == "data") {
if (this.inherited_x && this.inherited_radius) {
return inherit;
}
else {
return this.sdist(this.renderer.xscale, this.x, this.radius);
}
}
else {
return this.inherited_radius ? inherit : to_screen(this.radius);
}
});
this._define_or_inherit_attr("max_sradius", () => max(this.sradius));
}
_paint(ctx, indices, data) {
const { sx, sy, sradius, start_angle, end_angle } = { ...this, ...data };
const anticlock = this.model.direction == "anticlock";
for (const i of indices) {
const sx_i = sx[i];
const sy_i = sy[i];
const sradius_i = sradius[i];
const start_angle_i = start_angle.get(i);
const end_angle_i = end_angle.get(i);
if (!isFinite(sx_i + sy_i + sradius_i + start_angle_i + end_angle_i)) {
continue;
}
ctx.beginPath();
ctx.arc(sx_i, sy_i, sradius_i, start_angle_i, end_angle_i, anticlock);
ctx.lineTo(sx_i, sy_i);
ctx.closePath();
this.visuals.fill.apply(ctx, i);
this.visuals.hatch.apply(ctx, i);
this.visuals.line.apply(ctx, i);
}
}
_hit_point(geometry) {
let dist, sx0, sx1, sy0, sy1;
const { sx, sy } = geometry;
const x = this.renderer.xscale.invert(sx);
const y = this.renderer.yscale.invert(sy);
// check diameter first
sx0 = sx - this.max_sradius;
sx1 = sx + this.max_sradius;
const [x0, x1] = this.renderer.xscale.r_invert(sx0, sx1);
sy0 = sy - this.max_sradius;
sy1 = sy + this.max_sradius;
const [y0, y1] = this.renderer.yscale.r_invert(sy0, sy1);
const candidates = [];
for (const i of this.index.indices({ x0, x1, y0, y1 })) {
const r2 = this.sradius[i] ** 2;
[sx0, sx1] = this.renderer.xscale.r_compute(x, this.x[i]);
[sy0, sy1] = this.renderer.yscale.r_compute(y, this.y[i]);
dist = (sx0 - sx1) ** 2 + (sy0 - sy1) ** 2;
if (dist <= r2) {
candidates.push(i);
}
}
const anticlock = this.model.direction == "anticlock";
const indices = [];
for (const i of candidates) {
// NOTE: minus the angle because JS uses non-mathy convention for angles
const angle = Math.atan2(sy - this.sy[i], sx - this.sx[i]);
const is_full_circle = Math.abs(this.start_angle.get(i) - this.end_angle.get(i)) >= 2 * Math.PI;
if (is_full_circle || angle_between(-angle, -this.start_angle.get(i), -this.end_angle.get(i), anticlock)) {
indices.push(i);
}
}
return new Selection({ indices });
}
draw_legend_for_index(ctx, bbox, index) {
generic_area_vector_legend(this.visuals, ctx, bbox, index);
}
scenterxy(i) {
const r = this.sradius[i] / 2;
const a = (this.start_angle.get(i) + this.end_angle.get(i)) / 2;
const scx = this.sx[i] + r * Math.cos(a);
const scy = this.sy[i] + r * Math.sin(a);
return [scx, scy];
}
}
export class Wedge extends XYGlyph {
static __name__ = "Wedge";
constructor(attrs) {
super(attrs);
}
static {
this.prototype.default_view = WedgeView;
this.mixins([LineVector, FillVector, HatchVector]);
this.define(({}) => ({
direction: [Direction, "anticlock"],
radius: [p.DistanceSpec, { field: "radius" }],
start_angle: [p.AngleSpec, { field: "start_angle" }],
end_angle: [p.AngleSpec, { field: "end_angle" }],
}));
}
}
//# sourceMappingURL=wedge.js.map