landscape-widget 0.2.0 → 0.3.0-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,26 @@
1
+ export var COLORING_OPTIONS;
2
+ (function (COLORING_OPTIONS) {
3
+ COLORING_OPTIONS["none"] = "none";
4
+ COLORING_OPTIONS["selectedFeatures"] = "selected-features";
5
+ COLORING_OPTIONS["selectedDataPoints"] = "selected-data-points";
6
+ })(COLORING_OPTIONS || (COLORING_OPTIONS = {}));
7
+ export var SIDEBAR_TABS;
8
+ (function (SIDEBAR_TABS) {
9
+ SIDEBAR_TABS[SIDEBAR_TABS["CONFIGURE"] = 0] = "CONFIGURE";
10
+ SIDEBAR_TABS[SIDEBAR_TABS["EXPLORE"] = 1] = "EXPLORE";
11
+ })(SIDEBAR_TABS || (SIDEBAR_TABS = {}));
12
+ export var COLORMAP_TYPE;
13
+ (function (COLORMAP_TYPE) {
14
+ COLORMAP_TYPE["sequential"] = "sequential";
15
+ COLORMAP_TYPE["diverging"] = "diverging";
16
+ COLORMAP_TYPE["discrete"] = "discrete";
17
+ })(COLORMAP_TYPE || (COLORMAP_TYPE = {}));
18
+ export var SELECTION_SOURCES;
19
+ (function (SELECTION_SOURCES) {
20
+ SELECTION_SOURCES["LANDSCAPE"] = "landscape";
21
+ SELECTION_SOURCES["EXPLORER"] = "explorer";
22
+ SELECTION_SOURCES["SCATTER_PLOT"] = "scatter-plot";
23
+ SELECTION_SOURCES["HISTOGRAM"] = "histogram";
24
+ SELECTION_SOURCES["GROUP"] = "group";
25
+ })(SELECTION_SOURCES || (SELECTION_SOURCES = {}));
26
+ //# sourceMappingURL=Analysis.types.js.map
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ // import Gradient from 'javascript-color-gradient';
3
+ // export const getGradientArray = (nodes: number): string[] =>
4
+ // new Gradient().setColorGradient('#3F2CAF', '#e9446a').setMidpoint(nodes).getColors();
5
+ //# sourceMappingURL=Analysis.utils.js.map
@@ -0,0 +1,147 @@
1
+ import { createEdgeWeightGetter } from 'graphology-utils/getters';
2
+ import { connectedComponents } from 'graphology-components';
3
+ import { PPN } from './enum/layout';
4
+ import { iterateNoIntercomponentRepel } from './IterateNoIntercomponentRepel';
5
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
6
+ const { assignLayoutChanges, graphToByteArrays } = require('graphology-layout-forceatlas2/helpers');
7
+ export const defaultSettings = {
8
+ linLogMode: false,
9
+ outboundAttractionDistribution: false,
10
+ adjustSizes: false,
11
+ edgeWeightInfluence: 1,
12
+ scalingRatio: 1,
13
+ strongGravityMode: false,
14
+ gravity: 1,
15
+ slowDown: 1,
16
+ barnesHutOptimize: false,
17
+ barnesHutTheta: 0.5,
18
+ };
19
+ export class ForceAtlasAnimator {
20
+ constructor(graph, layoutParams, config) {
21
+ this.graph = graph;
22
+ this.graph_components = connectedComponents(graph);
23
+ this.params = layoutParams;
24
+ this.frameID = null;
25
+ this.running = false;
26
+ // the default edge weight getter required by graphToByteArrays
27
+ this.getEdgeWeight = createEdgeWeightGetter(undefined).fromEntry;
28
+ this.graphArrays = graphToByteArrays(graph, this.getEdgeWeight);
29
+ this.outputReducer = null;
30
+ this.niters = 0;
31
+ this.maxiters = config.maxiters;
32
+ this.miniters = config.miniters;
33
+ this.convergenceThreshold = config.convergenceThreshold;
34
+ this.frameListeners = [];
35
+ }
36
+ isRunning() {
37
+ return this.running;
38
+ }
39
+ resetNIters() {
40
+ this.niters = 0;
41
+ }
42
+ updateNodePropsArrays(graphArrays) {
43
+ // updates the arrays of node positions and properties
44
+ // must guarantee this.graphArrays is defined before calling
45
+ // number of array entries per node
46
+ let i = 0;
47
+ const _graphArrays = graphArrays;
48
+ this.graph.forEachNode((_, attr) => {
49
+ if (attr.fixed) {
50
+ _graphArrays.nodes[i] = attr.x;
51
+ _graphArrays.nodes[i + 1] = attr.y;
52
+ }
53
+ // turns out that keeping the dx/dy state across time points makes for a fairly ugly animation.
54
+ // probably makes it converge to a good stable state faster, though.
55
+ _graphArrays.nodes[i + 2] = 0; // dx
56
+ _graphArrays.nodes[i + 3] = 0; // dy
57
+ _graphArrays.nodes[i + 4] = 0; // old_dx
58
+ _graphArrays.nodes[i + 5] = 0; // old_dy
59
+ _graphArrays.nodes[i + 9] = attr.fixed ? 1 : 0;
60
+ i += PPN;
61
+ });
62
+ return _graphArrays;
63
+ }
64
+ forceNodePositionUpdate() {
65
+ const _graphArrays = this.graphArrays;
66
+ if (!_graphArrays)
67
+ return;
68
+ let i = 0;
69
+ this.graph.forEachNode((_, attr) => {
70
+ _graphArrays.nodes[i] = attr.x;
71
+ _graphArrays.nodes[i + 1] = attr.y;
72
+ _graphArrays.nodes[i + 2] = 0; // dx
73
+ _graphArrays.nodes[i + 3] = 0; // dy
74
+ _graphArrays.nodes[i + 4] = 0; // old_dx
75
+ _graphArrays.nodes[i + 5] = 0; // old_dy
76
+ i += PPN;
77
+ });
78
+ }
79
+ hasConverged() {
80
+ let i = 0;
81
+ if (!this.graphArrays) {
82
+ return false;
83
+ }
84
+ for (i = 0; i < this.graphArrays.nodes.length; i += PPN) {
85
+ // this is called NODE_CONVERGENCE in the code, and is computed from the node speed, but I don't actually know what it means.
86
+ if (this.graphArrays.nodes[i + 7] > this.convergenceThreshold) {
87
+ return false;
88
+ }
89
+ }
90
+ return true;
91
+ }
92
+ onFrame(listener) {
93
+ this.frameListeners.push(listener);
94
+ }
95
+ runFrame() {
96
+ let graphArrays;
97
+ if (this.graphArrays) {
98
+ graphArrays = this.updateNodePropsArrays(this.graphArrays);
99
+ }
100
+ else {
101
+ graphArrays = graphToByteArrays(this.graph, this.getEdgeWeight);
102
+ this.graphArrays = graphArrays;
103
+ }
104
+ if (this.params.settings !== undefined) {
105
+ iterateNoIntercomponentRepel(this.params.settings, graphArrays.nodes, graphArrays.edges, this.graph_components);
106
+ assignLayoutChanges(this.graph, graphArrays.nodes, this.outputReducer);
107
+ this.frameListeners.forEach((f) => f());
108
+ this.niters += 1;
109
+ // TODO: add convergence check. there is a nodewise "convergence" value whose meaning I don't understand
110
+ if (this.niters > this.maxiters) {
111
+ this.stop();
112
+ }
113
+ else if (this.niters > this.miniters && this.hasConverged()) {
114
+ this.stop();
115
+ }
116
+ else {
117
+ this.frameID = window.requestAnimationFrame(() => this.runFrame());
118
+ }
119
+ }
120
+ else {
121
+ console.error(`No Settings Provided`);
122
+ // This is very unexpected.
123
+ this.stop();
124
+ }
125
+ }
126
+ stop() {
127
+ this.running = false;
128
+ this.resetNIters();
129
+ if (this.frameID !== null) {
130
+ window.cancelAnimationFrame(this.frameID);
131
+ this.frameID = null;
132
+ }
133
+ return this;
134
+ }
135
+ start() {
136
+ if (this.running)
137
+ return;
138
+ this.running = true;
139
+ this.runFrame();
140
+ }
141
+ kill() {
142
+ this.stop();
143
+ this.frameListeners = [];
144
+ delete this.graphArrays;
145
+ }
146
+ }
147
+ //# sourceMappingURL=ForceAtlasAnimator.js.map
@@ -0,0 +1,105 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
13
+ import classNames from 'classnames';
14
+ import { ControlsContainer, SigmaContainer, useSigma, useCamera } from '@react-sigma/core';
15
+ import { Tooltip } from './mui/Tooltip';
16
+ import { Lasso } from './icons/Lasso';
17
+ import { Plus, Minus, Reset, FullScreenEnter, FullScreenExit, ClearSelection, Invert, } from './icons';
18
+ import { LoadGraph } from './LoadGraph';
19
+ import { SigmaLasso } from './SigmaLasso';
20
+ import '@react-sigma/core/lib/react-sigma.min.css';
21
+ import './GraphLayout.scss';
22
+ export const GraphContainer = ({ graph, animate, animator, selectedNodes, setSelectedNodes, addSelectedNodes, isShowColorLegend, colorLegend, isFullScreen, toggleFullScreen, shouldLayoutUpdate, setShouldLayoutUpdate, clearSigma, setClearSigma, }) => {
23
+ const [isLassoActive, setIsLassoActive] = useState(false);
24
+ const { zoomIn, zoomOut, reset } = useCamera({ duration: 200, factor: 1.5 });
25
+ const sigma = useSigma();
26
+ useEffect(() => {
27
+ if (shouldLayoutUpdate && setShouldLayoutUpdate) {
28
+ new Promise((resolve) => {
29
+ resolve(sigma.refresh());
30
+ }).then(() => {
31
+ setShouldLayoutUpdate(false);
32
+ });
33
+ }
34
+ }, [shouldLayoutUpdate]);
35
+ const onClearSelection = useCallback(() => {
36
+ setSelectedNodes([]);
37
+ }, [setSelectedNodes]);
38
+ const onInvertSelection = useCallback(() => {
39
+ const newSelectedNodes = [];
40
+ graph.forEachNode((node, attr) => {
41
+ const nodeID = Number(node);
42
+ if (!selectedNodes.some((n) => n === nodeID)) {
43
+ newSelectedNodes.push(nodeID);
44
+ }
45
+ });
46
+ setSelectedNodes(newSelectedNodes);
47
+ }, [selectedNodes, setSelectedNodes]);
48
+ useEffect(() => {
49
+ sigma.refresh();
50
+ }, [sigma, isFullScreen]);
51
+ useEffect(() => {
52
+ if (sigma && clearSigma) {
53
+ sigma.clear();
54
+ }
55
+ setClearSigma(false);
56
+ }, [clearSigma]);
57
+ return (!!graph &&
58
+ !!animator && (React.createElement(React.Fragment, null,
59
+ React.createElement(LoadGraph, { graph: graph, animator: animator, animate: animate, selectedNodes: selectedNodes, setSelectedNodes: setSelectedNodes, addSelectedNodes: addSelectedNodes }),
60
+ React.createElement(SigmaLasso, { active: isLassoActive, setSelectedNodes: setSelectedNodes, addSelectedNodes: addSelectedNodes }),
61
+ React.createElement(ControlsContainer, { position: 'bottom-right' },
62
+ React.createElement(Tooltip, { title: 'Zoom in', placement: 'top' },
63
+ React.createElement("div", { className: 'react-sigma-control' },
64
+ React.createElement("button", { type: 'button', className: 'zoom-in', onClick: () => zoomIn() },
65
+ React.createElement(Plus, null)))),
66
+ React.createElement(Tooltip, { title: 'Zoom out', placement: 'top' },
67
+ React.createElement("div", { className: 'react-sigma-control' },
68
+ React.createElement("button", { type: 'button', className: 'zoom-out', onClick: () => zoomOut() },
69
+ React.createElement(Minus, null)))),
70
+ React.createElement(Tooltip, { title: isFullScreen ? 'Exit fullscreen' : 'Enter fullscreen', placement: 'top' },
71
+ React.createElement("div", { className: 'react-sigma-control' },
72
+ React.createElement("button", { type: 'button', onClick: toggleFullScreen }, isFullScreen ? React.createElement(FullScreenExit, null) : React.createElement(FullScreenEnter, null)))),
73
+ React.createElement(Tooltip, { title: 'Lasso selection', placement: 'top' },
74
+ React.createElement("div", { className: 'react-sigma-control' },
75
+ React.createElement("button", { type: 'button', className: classNames('lasso-selection', {
76
+ active: isLassoActive,
77
+ }), onClick: () => setIsLassoActive((prevState) => !prevState) },
78
+ React.createElement(Lasso, null)))),
79
+ React.createElement(Tooltip, { title: 'Reset scale', placement: 'top' },
80
+ React.createElement("div", { className: 'react-sigma-control' },
81
+ React.createElement("button", { type: 'button', className: 'reset-scale', onClick: () => reset() },
82
+ React.createElement(Reset, null)))),
83
+ React.createElement(Tooltip, { title: 'Clear selection', placement: 'top' },
84
+ React.createElement("div", { className: 'react-sigma-control' },
85
+ React.createElement("button", { type: 'button', disabled: selectedNodes.length === 0, className: 'clear-selection', onClick: onClearSelection },
86
+ React.createElement(ClearSelection, null)))),
87
+ React.createElement(Tooltip, { title: 'Invert selection', placement: 'top' },
88
+ React.createElement("div", { className: 'react-sigma-control' },
89
+ React.createElement("button", { type: 'button', className: 'invert-selection', onClick: onInvertSelection },
90
+ React.createElement(Invert, null))))))));
91
+ };
92
+ export const GraphWrapper = (_a) => {
93
+ var { isFullScreen } = _a, props = __rest(_a, ["isFullScreen"]);
94
+ const fullScreenStyles = useMemo(() => isFullScreen
95
+ ? {
96
+ position: 'fixed',
97
+ top: 0,
98
+ left: 0,
99
+ zIndex: 1400,
100
+ }
101
+ : {}, [isFullScreen]);
102
+ return (React.createElement(SigmaContainer, { id: 'graph-container', style: fullScreenStyles },
103
+ React.createElement(GraphContainer, Object.assign({ isFullScreen: isFullScreen }, props))));
104
+ };
105
+ //# sourceMappingURL=GraphContainer.js.map