UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

268 lines 13 kB
import { Model } from "../../model"; import { index_of, map } from "../../core/util/arrayable"; import { contains, uniq } from "../../core/util/array"; import { dict } from "../../core/util/object"; import { Selection } from "../selections/selection"; export class GraphHitTestPolicy extends Model { static __name__ = "GraphHitTestPolicy"; constructor(attrs) { super(attrs); } _hit_test(geometry, graph_view, renderer_view) { if (!graph_view.model.visible) { return null; } const hit_test_result = renderer_view.glyph.hit_test(geometry); if (hit_test_result == null) { return null; } else { return renderer_view.model.view.convert_selection_from_subset(hit_test_result); } } } export class EdgesOnly extends GraphHitTestPolicy { static __name__ = "EdgesOnly"; constructor(attrs) { super(attrs); } hit_test(geometry, graph_view) { return this._hit_test(geometry, graph_view, graph_view.edge_view); } do_selection(hit_test_result, graph, final, mode) { if (hit_test_result == null) { return false; } const edge_selection = graph.edge_renderer.data_source.selected; edge_selection.update(hit_test_result, final, mode); graph.edge_renderer.data_source._select.emit(); return !edge_selection.is_empty(); } do_inspection(hit_test_result, geometry, graph_view, final, mode) { if (hit_test_result == null) { return false; } const { edge_renderer } = graph_view.model; const edge_inspection = edge_renderer.get_selection_manager().get_or_create_inspector(graph_view.edge_view.model); edge_inspection.update(hit_test_result, final, mode); // silently set inspected attr to avoid triggering data_source.change event and rerender graph_view.edge_view.model.data_source.setv({ inspected: edge_inspection }, { silent: true }); graph_view.edge_view.model.data_source.inspect.emit([graph_view.edge_view.model, { geometry }]); return !edge_inspection.is_empty(); } } export class NodesOnly extends GraphHitTestPolicy { static __name__ = "NodesOnly"; constructor(attrs) { super(attrs); } hit_test(geometry, graph_view) { return this._hit_test(geometry, graph_view, graph_view.node_view); } do_selection(hit_test_result, graph, final, mode) { if (hit_test_result == null) { return false; } const node_selection = graph.node_renderer.data_source.selected; node_selection.update(hit_test_result, final, mode); graph.node_renderer.data_source._select.emit(); return !node_selection.is_empty(); } do_inspection(hit_test_result, geometry, graph_view, final, mode) { if (hit_test_result == null) { return false; } const { node_renderer } = graph_view.model; const node_inspection = node_renderer.get_selection_manager().get_or_create_inspector(graph_view.node_view.model); node_inspection.update(hit_test_result, final, mode); // silently set inspected attr to avoid triggering data_source.change event and rerender graph_view.node_view.model.data_source.setv({ inspected: node_inspection }, { silent: true }); graph_view.node_view.model.data_source.inspect.emit([graph_view.node_view.model, { geometry }]); return !node_inspection.is_empty(); } } export class NodesAndLinkedEdges extends GraphHitTestPolicy { static __name__ = "NodesAndLinkedEdges"; constructor(attrs) { super(attrs); } hit_test(geometry, graph_view) { return this._hit_test(geometry, graph_view, graph_view.node_view); } get_linked_edges(node_source, edge_source, mode) { const node_data = dict(node_source.data); const index = node_data.get("index") ?? []; const node_indices = (() => { switch (mode) { case "selection": return map(node_source.selected.indices, (i) => index[i]); case "inspection": return map(node_source.inspected.indices, (i) => index[i]); } })(); const edge_data = dict(edge_source.data); const start = edge_data.get("start") ?? []; const end = edge_data.get("end") ?? []; const edge_indices = []; const n = start.length; for (let i = 0; i < n; i++) { if (contains(node_indices, start[i]) || contains(node_indices, end[i])) { edge_indices.push(i); } } const linked_edges = new Selection(); for (const i of edge_indices) { linked_edges.multiline_indices.set(i, [0]); //currently only supports 2-element multilines, so this is all of it } linked_edges.indices = edge_indices; return linked_edges; } do_selection(hit_test_result, graph, final, mode) { if (hit_test_result == null) { return false; } const node_selection = graph.node_renderer.data_source.selected; node_selection.update(hit_test_result, final, mode); const edge_selection = graph.edge_renderer.data_source.selected; const linked_edges_selection = this.get_linked_edges(graph.node_renderer.data_source, graph.edge_renderer.data_source, "selection"); edge_selection.update(linked_edges_selection, final, mode); graph.node_renderer.data_source._select.emit(); return !node_selection.is_empty(); } do_inspection(hit_test_result, geometry, graph_view, final, mode) { if (hit_test_result == null) { return false; } const node_inspection = graph_view.node_view.model.data_source.selection_manager.get_or_create_inspector(graph_view.node_view.model); node_inspection.update(hit_test_result, final, mode); graph_view.node_view.model.data_source.setv({ inspected: node_inspection }, { silent: true }); const edge_inspection = graph_view.edge_view.model.data_source.selection_manager.get_or_create_inspector(graph_view.edge_view.model); const linked_edges = this.get_linked_edges(graph_view.node_view.model.data_source, graph_view.edge_view.model.data_source, "inspection"); edge_inspection.update(linked_edges, final, mode); //silently set inspected attr to avoid triggering data_source.change event and rerender graph_view.edge_view.model.data_source.setv({ inspected: edge_inspection }, { silent: true }); graph_view.node_view.model.data_source.inspect.emit([graph_view.node_view.model, { geometry }]); return !node_inspection.is_empty(); } } export class EdgesAndLinkedNodes extends GraphHitTestPolicy { static __name__ = "EdgesAndLinkedNodes"; constructor(attrs) { super(attrs); } hit_test(geometry, graph_view) { return this._hit_test(geometry, graph_view, graph_view.edge_view); } get_linked_nodes(node_source, edge_source, mode) { const edge_indices = (() => { switch (mode) { case "selection": return edge_source.selected.indices; case "inspection": return edge_source.inspected.indices; } })(); const edge_data = dict(edge_source.data); const start = edge_data.get("start") ?? []; const end = edge_data.get("end") ?? []; const nodes = []; for (const i of edge_indices) { nodes.push(start[i], end[i]); } const node_data = dict(node_source.data); const index = node_data.get("index") ?? []; const node_indices = uniq(nodes).map((i) => index_of(index, i)); return new Selection({ indices: node_indices }); } do_selection(hit_test_result, graph, final, mode) { if (hit_test_result == null) { return false; } const edge_selection = graph.edge_renderer.data_source.selected; edge_selection.update(hit_test_result, final, mode); const node_selection = graph.node_renderer.data_source.selected; const linked_nodes = this.get_linked_nodes(graph.node_renderer.data_source, graph.edge_renderer.data_source, "selection"); node_selection.update(linked_nodes, final, mode); graph.edge_renderer.data_source._select.emit(); return !edge_selection.is_empty(); } do_inspection(hit_test_result, geometry, graph_view, final, mode) { if (hit_test_result == null) { return false; } const edge_inspection = graph_view.edge_view.model.data_source.selection_manager.get_or_create_inspector(graph_view.edge_view.model); edge_inspection.update(hit_test_result, final, mode); graph_view.edge_view.model.data_source.setv({ inspected: edge_inspection }, { silent: true }); const node_inspection = graph_view.node_view.model.data_source.selection_manager.get_or_create_inspector(graph_view.node_view.model); const linked_nodes = this.get_linked_nodes(graph_view.node_view.model.data_source, graph_view.edge_view.model.data_source, "inspection"); node_inspection.update(linked_nodes, final, mode); // silently set inspected attr to avoid triggering data_source.change event and rerender graph_view.node_view.model.data_source.setv({ inspected: node_inspection }, { silent: true }); graph_view.edge_view.model.data_source.inspect.emit([graph_view.edge_view.model, { geometry }]); return !edge_inspection.is_empty(); } } export class NodesAndAdjacentNodes extends GraphHitTestPolicy { static __name__ = "NodesAndAdjacentNodes"; constructor(attrs) { super(attrs); } hit_test(geometry, graph_view) { return this._hit_test(geometry, graph_view, graph_view.node_view); } get_adjacent_nodes(node_source, edge_source, mode) { const node_data = dict(node_source.data); const index = node_data.get("index") ?? []; const selected_node_indices = (() => { switch (mode) { case "selection": return map(node_source.selected.indices, (i) => index[i]); case "inspection": return map(node_source.inspected.indices, (i) => index[i]); } })(); const edge_data = dict(edge_source.data); const start = edge_data.get("start") ?? []; const end = edge_data.get("end") ?? []; const adjacent_nodes = []; const selected_nodes = []; for (let i = 0; i < start.length; i++) { if (contains(selected_node_indices, start[i])) { adjacent_nodes.push(end[i]); selected_nodes.push(start[i]); } if (contains(selected_node_indices, end[i])) { adjacent_nodes.push(start[i]); selected_nodes.push(end[i]); } } for (let i = 0; i < selected_nodes.length; i++) { adjacent_nodes.push(selected_nodes[i]); } const adjacent_node_indices = uniq(adjacent_nodes).map((i) => index_of(index, i)); return new Selection({ indices: adjacent_node_indices }); } do_selection(hit_test_result, graph, final, mode) { if (hit_test_result == null) { return false; } const node_selection = graph.node_renderer.data_source.selected; node_selection.update(hit_test_result, final, mode); const adjacent_nodes_selection = this.get_adjacent_nodes(graph.node_renderer.data_source, graph.edge_renderer.data_source, "selection"); if (!adjacent_nodes_selection.is_empty()) { node_selection.update(adjacent_nodes_selection, final, mode); } graph.node_renderer.data_source._select.emit(); return !node_selection.is_empty(); } do_inspection(hit_test_result, geometry, graph_view, final, mode) { if (hit_test_result == null) { return false; } const node_inspection = graph_view.node_view.model.data_source.selection_manager.get_or_create_inspector(graph_view.node_view.model); node_inspection.update(hit_test_result, final, mode); graph_view.node_view.model.data_source.setv({ inspected: node_inspection }, { silent: true }); const adjacent_nodes_inspection = this.get_adjacent_nodes(graph_view.node_view.model.data_source, graph_view.edge_view.model.data_source, "inspection"); if (!adjacent_nodes_inspection.is_empty()) { node_inspection.update(adjacent_nodes_inspection, final, mode); graph_view.node_view.model.data_source.setv({ inspected: node_inspection }, { silent: true }); } graph_view.node_view.model.data_source.inspect.emit([graph_view.node_view.model, { geometry }]); return !node_inspection.is_empty(); } } //# sourceMappingURL=graph_hit_test_policy.js.map