UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

177 lines 7.5 kB
import { Model } from "../../model"; import { union, intersection, difference, symmetric_difference } from "../../core/util/array"; import { merge } from "../../core/util/object"; import { Arrayable, Int, Mapping } from "../../core/kinds"; import { map } from "../../core/util/arrayable"; export const OpaqueIndices = Arrayable(Int); export const MultiIndices = Mapping(Int, OpaqueIndices); export class Selection extends Model { static __name__ = "Selection"; constructor(attrs) { super(attrs); } get_view() { return this.view; } static { this.define(({ Int, List, Struct }) => ({ indices: [OpaqueIndices, []], line_indices: [OpaqueIndices, []], multiline_indices: [MultiIndices, new Map()], image_indices: [List(Struct({ index: Int, i: Int, j: Int, flat_index: Int })), []], })); this.internal(({ List, AnyRef, Nullable }) => ({ selected_glyphs: [List(AnyRef()), []], view: [Nullable(AnyRef()), null], })); } get selected_glyph() { return this.selected_glyphs.length > 0 ? this.selected_glyphs[0] : null; } add_to_selected_glyphs(glyph) { this.selected_glyphs.push(glyph); } update(selection, _final = true, mode = "replace") { switch (mode) { case "replace": { this.update_through_replacement(selection); break; } case "toggle": { this.update_through_toggle(selection); break; } case "append": { this.update_through_union(selection); break; } case "intersect": { this.update_through_intersection(selection); break; } case "subtract": { this.update_through_subtraction(selection); break; } case "xor": { this.update_through_symmetric_difference(selection); break; } } } // TODO `size` wouldn't be needed if `indices` was an instance of // `Indices` class, instead of an array of numbers. Then also we // could just call `.invert()` on the class. invert(size) { const indices = new Set(this.indices); const inversion = []; for (let i = 0; i < size; i++) { if (!indices.has(i)) { inversion.push(i); } } this.indices = inversion; // this.line_indices // this.multiline_indices // this.image_indices } clear() { this.indices = []; this.line_indices = []; this.multiline_indices = new Map(); this.image_indices = []; this.view = null; this.selected_glyphs = []; } map(mapper) { return new Selection({ ...this.attributes, indices: map(this.indices, mapper), // NOTE: line_indices don't support subset indexing multiline_indices: new Map(map([...this.multiline_indices.entries()], ([index, line_indices]) => [mapper(index), line_indices])), image_indices: this.image_indices.map((image_index) => ({ ...image_index, index: mapper(image_index.index) })), }); } is_empty() { return this.indices.length == 0 && this.line_indices.length == 0 && this.image_indices.length == 0; } _union_image_indices(...collection) { const is = new Map(); const js = new Map(); const result = []; for (const indices of collection) { for (const image_index of indices) { const { index, i, j } = image_index; const iis = is.get(index); const ijs = js.get(index); if (iis != null && ijs != null) { if (!iis.has(i) || !ijs.has(j)) { result.push(image_index); iis.add(i); ijs.add(j); } } else { result.push(image_index); is.set(index, new Set([i])); js.set(index, new Set([j])); } } } return result; } update_through_replacement(other) { this.indices = other.indices; this.line_indices = other.line_indices; this.multiline_indices = other.multiline_indices; this.image_indices = other.image_indices; this.view = other.view; this.selected_glyphs = other.selected_glyphs; } update_through_toggle(other) { // note the order of arguments when comparing with update_through_subtraction() this.indices = difference(other.indices, this.indices); // TODO: think through and fix any logic below this.selected_glyphs = union(other.selected_glyphs, this.selected_glyphs); this.line_indices = union(other.line_indices, this.line_indices); this.image_indices = this._union_image_indices(this.image_indices, other.image_indices); // TODO this.view = other.view; this.multiline_indices = merge(other.multiline_indices, this.multiline_indices); } update_through_union(other) { this.indices = union(this.indices, other.indices); this.selected_glyphs = union(other.selected_glyphs, this.selected_glyphs); this.line_indices = union(other.line_indices, this.line_indices); this.image_indices = this._union_image_indices(this.image_indices, other.image_indices); // TODO this.view = other.view; this.multiline_indices = merge(other.multiline_indices, this.multiline_indices); } update_through_intersection(other) { this.indices = intersection(this.indices, other.indices); // TODO: think through and fix any logic below this.selected_glyphs = union(other.selected_glyphs, this.selected_glyphs); this.line_indices = union(other.line_indices, this.line_indices); this.image_indices = this._union_image_indices(this.image_indices, other.image_indices); // TODO this.view = other.view; this.multiline_indices = merge(other.multiline_indices, this.multiline_indices); } update_through_subtraction(other) { this.indices = difference(this.indices, other.indices); // TODO: think through and fix any logic below this.selected_glyphs = union(other.selected_glyphs, this.selected_glyphs); this.line_indices = union(other.line_indices, this.line_indices); this.image_indices = this._union_image_indices(this.image_indices, other.image_indices); // TODO this.view = other.view; this.multiline_indices = merge(other.multiline_indices, this.multiline_indices); } update_through_symmetric_difference(other) { this.indices = symmetric_difference(this.indices, other.indices); // TODO: think through and fix any logic below this.selected_glyphs = union(other.selected_glyphs, this.selected_glyphs); this.line_indices = union(other.line_indices, this.line_indices); this.image_indices = this._union_image_indices(this.image_indices, other.image_indices); // TODO this.view = other.view; this.multiline_indices = merge(other.multiline_indices, this.multiline_indices); } } //# sourceMappingURL=selection.js.map