@bokeh/bokehjs
Version:
Interactive, novel data visualization
85 lines • 2.99 kB
JavaScript
import { XYGlyph, XYGlyphView } from "./xy_glyph";
import { generic_area_scalar_legend } from "./utils";
import * as hittest from "../../core/hittest";
import * as mixins from "../../core/property_mixins";
import { Selection } from "../selections/selection";
export class PatchView extends XYGlyphView {
static __name__ = "PatchView";
_paint(ctx, indices, data) {
const { sx, sy } = { ...this, ...data };
let move = true;
ctx.beginPath();
for (const i of indices) {
const sx_i = sx[i];
const sy_i = sy[i];
if (!isFinite(sx_i + sy_i)) {
ctx.closePath();
move = true;
}
else {
if (move) {
ctx.moveTo(sx_i, sy_i);
move = false;
}
else {
ctx.lineTo(sx_i, sy_i);
}
}
}
ctx.closePath();
this.visuals.fill.apply(ctx, "evenodd");
this.visuals.hatch.apply(ctx, "evenodd");
this.visuals.line.apply(ctx);
}
draw_legend_for_index(ctx, bbox, _index) {
generic_area_scalar_legend(this.visuals, ctx, bbox);
}
_hit_point(geometry) {
const result = new Selection();
const { sx, sy } = geometry;
// Collect NaN-separated sub-paths
const sub_paths_sx = [];
const sub_paths_sy = [];
const n = this.sx.length;
let k = 0;
for (let j = 0; j <= n; j++) {
if (j == n || isNaN(this.sx[j])) {
if (j > k) {
// Use subarray to create views (like patches.ts does)
sub_paths_sx.push(this.sx.subarray(k, j));
sub_paths_sy.push(this.sy.subarray(k, j));
}
k = j + 1;
}
}
if (sub_paths_sx.length == 0) {
return result;
}
// Use "evenodd" fill rule (matches Canvas2D rendering):
// A point is inside the filled region if it's contained by an odd number of sub-paths.
// This handles both holes (even count = outside) and disjoint polygons (each adds to count).
let inside_count = 0;
for (let i = 0; i < sub_paths_sx.length; i++) {
if (hittest.point_in_poly(sx, sy, sub_paths_sx[i], sub_paths_sy[i])) {
inside_count++;
}
}
// Odd count = inside filled region, Even count = inside hole or outside
if (inside_count % 2 === 1) {
result.add_to_selected_glyphs(this.model);
result.view = this;
}
return result;
}
}
export class Patch extends XYGlyph {
static __name__ = "Patch";
constructor(attrs) {
super(attrs);
}
static {
this.prototype.default_view = PatchView;
this.mixins([mixins.LineScalar, mixins.FillScalar, mixins.HatchScalar]);
}
}
//# sourceMappingURL=patch.js.map