@bokeh/bokehjs
Version:
Interactive, novel data visualization
136 lines • 5.1 kB
JavaScript
import { Area, AreaView } from "./area";
import * as hittest from "../../core/hittest";
import * as p from "../../core/properties";
import { StepMode } from "../../core/enums";
import { flip_step_mode } from "../../core/util/flip_step_mode";
import { Selection } from "../selections/selection";
export class VAreaStepView extends AreaView {
static __name__ = "VAreaStepView";
_index_data(index) {
const { min, max } = Math;
const { x, y1, y2 } = this;
for (let i = 0; i < this.data_size; i++) {
const x_i = x[i];
const y1_i = y1[i];
const y2_i = y2[i];
index.add_rect(x_i, min(y1_i, y2_i), x_i, max(y1_i, y2_i));
}
}
_step_path(ctx, mode, sx, sy, from_i, to_i) {
// Assume the path was already moved to the first point
let prev_x = sx[from_i];
let prev_y = sy[from_i];
const idx_dir = from_i < to_i ? 1 : -1;
for (let i = from_i + idx_dir; i != to_i; i += idx_dir) {
switch (mode) {
case "before": {
ctx.lineTo(prev_x, sy[i]);
ctx.lineTo(sx[i], sy[i]);
break;
}
case "after": {
ctx.lineTo(sx[i], prev_y);
ctx.lineTo(sx[i], sy[i]);
break;
}
case "center": {
const mid_x = (prev_x + sx[i]) / 2;
ctx.lineTo(mid_x, prev_y);
ctx.lineTo(mid_x, sy[i]);
ctx.lineTo(sx[i], sy[i]);
break;
}
}
prev_x = sx[i];
prev_y = sy[i];
}
}
_paint(ctx, _indices, data) {
const { sx, sy1, sy2 } = { ...this, ...data };
const forward_mode = this.model.step_mode;
const backward_mode = flip_step_mode(this.model.step_mode);
ctx.beginPath();
ctx.moveTo(sx[0], sy1[0]);
this._step_path(ctx, forward_mode, sx, sy1, 0, sx.length);
this._step_path(ctx, backward_mode, sx, sy2, sx.length, -1);
ctx.closePath();
this.visuals.fill.apply(ctx);
this.visuals.hatch.apply(ctx);
}
scenterxy(i) {
const scx = this.sx[i];
const scy = (this.sy1[i] + this.sy2[i]) / 2;
return [scx, scy];
}
_line_selection_for(i) {
return new Selection({ line_indices: [i], selected_glyphs: [this.model], view: this });
}
_hit_point_before(geometry) {
const { sx, sy1, sy2 } = this;
for (let i = 1; i < this.data_size; i++) {
const px = [sx[i - 1], sx[i], sx[i], sx[i - 1]];
const py = [sy1[i], sy1[i], sy2[i], sy2[i]];
if (hittest.point_in_poly(geometry.sx, geometry.sy, px, py)) {
return this._line_selection_for(i);
}
}
return new Selection();
}
_hit_point_after(geometry) {
const { sx, sy1, sy2 } = this;
for (let i = 0; i < this.data_size - 1; i++) {
const px = [sx[i], sx[i + 1], sx[i + 1], sx[i]];
const py = [sy1[i], sy1[i], sy2[i], sy2[i]];
if (hittest.point_in_poly(geometry.sx, geometry.sy, px, py)) {
return this._line_selection_for(i);
}
}
return new Selection();
}
_hit_point_center(geometry) {
const { sx, sy1, sy2 } = this;
for (let i = 0; i < this.data_size; i++) {
const mid_prev_x = (sx[i - 1] + sx[i]) / 2; /* undefined for first */
const mid_next_x = (sx[i] + sx[i + 1]) / 2; /* undefined for last */
const px = (() => {
if (i == 0) {
return [sx[i], mid_next_x, mid_next_x, sx[i]];
}
else if (i == this.data_size - 1) {
return [mid_prev_x, sx[i], sx[i], mid_prev_x];
}
else {
return [mid_prev_x, mid_next_x, mid_next_x, mid_prev_x];
}
})();
const py = [sy1[i], sy1[i], sy2[i], sy2[i]];
if (hittest.point_in_poly(geometry.sx, geometry.sy, px, py)) {
return this._line_selection_for(i);
}
}
return new Selection();
}
_hit_point(geometry) {
switch (this.model.step_mode) {
case "before": return this._hit_point_before(geometry);
case "after": return this._hit_point_after(geometry);
case "center": return this._hit_point_center(geometry);
}
}
}
export class VAreaStep extends Area {
static __name__ = "VAreaStep";
constructor(attrs) {
super(attrs);
}
static {
this.prototype.default_view = VAreaStepView;
this.define(({}) => ({
x: [p.XCoordinateSpec, { field: "x" }],
y1: [p.YCoordinateSpec, { field: "y1" }],
y2: [p.YCoordinateSpec, { field: "y2" }],
step_mode: [StepMode, "before"],
}));
}
}
//# sourceMappingURL=varea_step.js.map