172 | );
173 | default:
174 | return ;
175 | }
176 | };
177 |
178 | // Proptypes of ToggleBurttons
179 | InputField.propTypes = {
180 | value: PropTypes.any.isRequired,
181 | type: PropTypes.string.isRequired,
182 | description: PropTypes.string.isRequired,
183 | action: PropTypes.func.isRequired,
184 | };
185 |
186 | export default InputField;
187 |
--------------------------------------------------------------------------------
/net2vis/src/components/legend/ComplexLegendItem.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "react-redux";
3 | import PropTypes from "prop-types";
4 |
5 | import EdgeComponent from "../network/EdgeComponent";
6 |
7 | import * as paths from "../../paths";
8 | import * as colors from "../../colors";
9 |
10 | class ComplexLegendItem extends React.Component {
11 | render() {
12 | var set = this.props.layer_types_settings[this.props.layer.layer.name];
13 | const noColors = this.props.preferences.no_colors.value;
14 | let isGroup = false;
15 | const selected = this.props.selected === this.props.layer.layer.name;
16 | for (var group in this.props.groups) {
17 | if (this.props.groups[group].name === this.props.layer.layer.name) {
18 | isGroup = true;
19 | }
20 | }
21 | const style = {
22 | fill: colors.getFillColor(set, noColors, isGroup),
23 | stroke: colors.getStrokeColor(set, noColors, isGroup, selected),
24 | strokeLinejoin: "round",
25 | strokeWidth: isGroup
26 | ? this.props.legend_preferences.stroke_width.value * 3
27 | : this.props.legend_preferences.stroke_width.value,
28 | };
29 | const extreme_dimensions = {
30 | max_size: this.props.legend_preferences.layer_height.value,
31 | min_size: this.props.legend_preferences.layer_height.value,
32 | }; // Get the Extremes of the Display Size for the Glyphs
33 | const pathableLayer = {
34 | layer: {
35 | id: parseInt(this.props.layer.id, 10),
36 | properties: {
37 | input: this.props.layer.layer.properties.input,
38 | output: this.props.layer.layer.properties.output,
39 | },
40 | },
41 | width: this.props.legend_preferences.layer_width.value,
42 | y: this.props.layer.y,
43 | };
44 | const pathData = paths.calculateGlyphPath(
45 | extreme_dimensions,
46 | [extreme_dimensions.max_size, extreme_dimensions.max_size],
47 | pathableLayer,
48 | this.props.edges
49 | ); // Calculate the Path of the Layer
50 | const current_edges = paths.getOutgoingEdges(
51 | pathableLayer,
52 | this.props.edges
53 | ); // Get relevant Edges going out from the current Layer
54 |
55 | return (
56 |
63 | {current_edges.map((edge, index) => (
64 |
73 | ))}
74 |
80 | this.props.action(this.props.layer.layer.name)}
84 | />
85 |
86 |
87 | );
88 | }
89 | }
90 |
91 | // PropTypes of this Class
92 | ComplexLegendItem.propTypes = {
93 | groups: PropTypes.array.isRequired,
94 | layer_types_settings: PropTypes.object.isRequired,
95 | legend_preferences: PropTypes.object.isRequired,
96 | preferences: PropTypes.object.isRequired,
97 | selected: PropTypes.string.isRequired,
98 | };
99 |
100 | // Map the State of the Application to the Props of this Class
101 | function mapStateToProps(state, ownProps) {
102 | return {
103 | groups: state.groups,
104 | layer_types_settings: state.layer_types_settings,
105 | legend_preferences: state.legend_preferences,
106 | preferences: state.preferences,
107 | };
108 | }
109 |
110 | export default connect(mapStateToProps, undefined)(ComplexLegendItem);
111 |
--------------------------------------------------------------------------------
/net2vis/src/components/legend/LegendItem.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "react-redux";
3 | import PropTypes from "prop-types";
4 |
5 | import * as legend from "../../legend";
6 | import * as colors from "../../colors";
7 |
8 | import ComplexLegendItem from "./ComplexLegendItem";
9 |
10 | class LegendItem extends React.Component {
11 | render() {
12 | // Set the properties of the item to be drawn
13 | const set = this.props.representation.layer.representer.setting;
14 | const noColors = this.props.preferences.no_colors.value;
15 | const isGroup = !this.props.representation.layer.trivial;
16 | const selected =
17 | this.props.selected === this.props.representation.layer.representer.name;
18 | const strokeColor = this.props.representation.layer.dense
19 | ? colors.getFillColor(set, noColors, isGroup)
20 | : colors.getStrokeColor(set, noColors, isGroup, selected);
21 | const style = {
22 | fill: colors.getFillColor(set, noColors, isGroup),
23 | stroke: this.props.representation.layer.active
24 | ? strokeColor
25 | : "lightgrey",
26 | strokeLinejoin: "round",
27 | strokeWidth: this.props.legend_preferences.stroke_width.value,
28 | };
29 | const textStyle = {
30 | fill: this.props.representation.layer.active ? "black" : "lightgrey",
31 | };
32 | if (this.props.representation.layer.trivial) {
33 | return (
34 |
35 |
44 | {this.props.representation.layer.representer.setting.alias}
45 |
46 |
53 | this.props.action(
54 | this.props.representation.layer.representer.name
55 | )
56 | }
57 | />
58 |
59 | );
60 | } else {
61 | const graph = this.props.representation.layer.graph;
62 | var nodes = [];
63 | graph.nodes().forEach(function (e) {
64 | nodes.push(graph.node(e));
65 | });
66 | var edges = [];
67 | graph.edges().forEach(function (e) {
68 | edges.push({ v: e.v, w: e.w, points: graph.edge(e) });
69 | });
70 | style.strokeWidth = this.props.legend_preferences.stroke_width.value * 3;
71 | var displacement =
72 | nodes[legend.getInputNode(nodes)].y -
73 | this.props.legend_preferences.layer_height.value / 2.0; // Calculate the displacement if an inputnode to a legenditem is not standardly placed in the legend
74 | return (
75 |
76 |
83 | this.props.action(
84 | this.props.representation.layer.representer.name
85 | )
86 | }
87 | />
88 |
98 | =
99 |
100 |
106 |
120 | {this.props.representation.layer.representer.setting.alias}
121 |
122 | {nodes.map((layer, i) => (
123 |
133 | ))}
134 |
135 |
136 | );
137 | }
138 | }
139 | }
140 |
141 | // PropTypes of this Class
142 | LegendItem.propTypes = {
143 | legend_preferences: PropTypes.object.isRequired,
144 | preferences: PropTypes.object.isRequired,
145 | selected: PropTypes.string.isRequired,
146 | };
147 |
148 | // Map the State of the Application to the Props of this Class
149 | function mapStateToProps(state, ownProps) {
150 | return {
151 | legend_preferences: state.legend_preferences,
152 | preferences: state.preferences,
153 | };
154 | }
155 |
156 | export default connect(mapStateToProps, undefined)(LegendItem);
157 |
--------------------------------------------------------------------------------
/net2vis/src/components/network/DimensionsLabelComponent.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | const DimensionsLabelComponent = ({
5 | dimensions,
6 | x,
7 | edge,
8 | layer_max_height,
9 | channels_first,
10 | }) => {
11 | var y_pos = edge.points[0].y; // Initialize the y_pos point Placeholder
12 | for (var j = 1; j < edge.points.length; j++) {
13 | // For all other Points
14 | if (y_pos === edge.points[j].y) {
15 | // The y_pos point had the same y value
16 | j = edge.points.length; // Break the Loop
17 | } else {
18 | // Not the same y value
19 | y_pos = edge.points[j].y; // Update the y_pos point Placeholder
20 | }
21 | }
22 | const initialIndex = channels_first ? 1 : 0;
23 | const lastIndex = channels_first ? dimensions.length : dimensions.length - 1;
24 | var dimTxt = dimensions[initialIndex]; // Initialize the dimensions Texts
25 | for (var i = initialIndex + 1; i < lastIndex; i++) {
26 | // For all dimensions except the last
27 | dimTxt = dimTxt + "x" + dimensions[i]; // Add it to the texts
28 | }
29 | const transform = `translate(${x}, ${5 + y_pos + layer_max_height / 2.0})`; // Manipulate the position of the graph
30 | return (
31 |
32 |
33 | {dimTxt}
34 |
35 |
36 | );
37 | };
38 |
39 | // Proptypes of ToggleBurttons
40 | DimensionsLabelComponent.propTypes = {
41 | dimensions: PropTypes.array.isRequired,
42 | x: PropTypes.number.isRequired,
43 | edge: PropTypes.object.isRequired,
44 | layer_max_height: PropTypes.number.isRequired,
45 | channels_first: PropTypes.bool.isRequired,
46 | };
47 |
48 | export default DimensionsLabelComponent;
49 |
--------------------------------------------------------------------------------
/net2vis/src/components/network/EdgeComponent.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | const EdgeComponent = ({
5 | edge,
6 | layer_max_height,
7 | horizontal_spacing,
8 | color,
9 | }) => {
10 | var points = JSON.parse(JSON.stringify(edge.points));
11 | var path = "";
12 | var y_pos = points[0].y; // Initialize the y_pos point Placeholder
13 | for (var j = 1; j < points.length; j++) {
14 | // For all other Points
15 | if (y_pos === points[j].y) {
16 | // The y_pos point had the same y value
17 | j = points.length; // Break the Loop
18 | } else {
19 | // Not the same y value
20 | y_pos = points[j].y; // Update the y_pos point Placeholder
21 | }
22 | }
23 | if (points[0].y !== y_pos) {
24 | // Check if the beggining of the path has a slope
25 | for (var i = 0; i < points.length; i++) {
26 | // Iterate over all points
27 | if (points[i].y === y_pos) {
28 | // The first point of the straight line part has been found
29 | points[i].x = points[i].x - horizontal_spacing.value / 2; // Change the x-value of this point to compensate the spacing
30 | i = points[i].length; // Exit the loop
31 | }
32 | }
33 | }
34 | if (points[points.length - 1].y !== y_pos) {
35 | // Check if the end of the path has a slope
36 | for (i = points.length - 1; i > 0; i--) {
37 | // Iterate over all points in reversed order
38 | if (points[i].y === y_pos) {
39 | // The last point of the straight line part has been found
40 | points[i].x = points[i].x + horizontal_spacing.value / 2; // Change the x-value of this point to compensate the spacing
41 | i = 0; // Exit the loop
42 | }
43 | }
44 | }
45 | for (i in points) {
46 | // Go over all Points
47 | if (points[i].y === y_pos) {
48 | // Check if on same y as y_pos
49 | path =
50 | path + points[i].x + "," + (points[i].y + layer_max_height / 2) + " "; // Add the Point to the Path
51 | }
52 | }
53 | return ;
54 | };
55 |
56 | // Proptypes of ToggleBurttons
57 | EdgeComponent.propTypes = {
58 | edge: PropTypes.object.isRequired,
59 | layer_max_height: PropTypes.number.isRequired,
60 | };
61 |
62 | export default EdgeComponent;
63 |
--------------------------------------------------------------------------------
/net2vis/src/components/network/EdgesComponent.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "react-redux";
3 | import PropTypes from "prop-types";
4 | import { bindActionCreators } from "redux";
5 |
6 | import * as actions from "../../actions";
7 |
8 | import EdgeComponent from "./EdgeComponent";
9 | import DimensionsLabelComponent from "./DimensionsLabelComponent";
10 |
11 | import * as paths from "../../paths";
12 |
13 | // Layer Component providing individual Layer Visualizations
14 | class Edges extends React.Component {
15 | // Render the Layer
16 | render() {
17 | // Get the Properties to use them in the Rendering
18 | const dimensions = this.props.layer.layer.properties.dimensions; // Get the Dimensions of the current Layer
19 | const current_edges = paths.getOutgoingEdges(
20 | this.props.layer,
21 | this.props.edges
22 | ); // Get relevant Edges going out from the current Layer
23 | const dimensionsLabelX =
24 | this.props.layer.x +
25 | this.props.layer.width / 2.0 +
26 | this.props.preferences.layers_spacing_horizontal.value / 2.0;
27 | var additionalLabelInput = undefined; // Placeholder for label if this is an input of the Net
28 | if (this.props.layer.layer.properties.input.length === 0) {
29 | // If no inputs
30 | additionalLabelInput = {
31 | dimensions: dimensions.in, // Dimensions for this label are the input dimensions of the layer
32 | x:
33 | 2.0 * this.props.layer.x -
34 | dimensionsLabelX -
35 | this.props.preferences.stroke_width.value, // X position of the label to be left of layer
36 | edge: {
37 | // Edge position is layer y
38 | points: [{ y: this.props.layer.y }],
39 | },
40 | };
41 | }
42 | var additionalLabelOutput = undefined; // Placeholder for label if this is an output of the net
43 | if (this.props.layer.layer.properties.output.length === 0) {
44 | // If no outputs
45 | additionalLabelOutput = {
46 | dimensions: dimensions.out, // Dimensions for this label are the output dimensions of the layer
47 | x: dimensionsLabelX, // X position of the label ro be right of layer
48 | edge: {
49 | // Edge position is layer y
50 | points: [{ y: this.props.layer.y }],
51 | },
52 | };
53 | }
54 | // Return a Shape with the calculated parameters and add the Property Tooltip
55 | return (
56 |
57 | {current_edges.map((edge, index) => (
58 |
59 |
69 | {this.props.preferences.show_dimensions.value &&
70 | dimensions.out.length > 1 && (
71 |
80 | )}
81 |
82 | ))}
83 | {this.props.preferences.show_dimensions.value &&
84 | additionalLabelInput !== undefined &&
85 | dimensions.out.length > 1 && (
86 |
95 | )}
96 | {this.props.preferences.show_dimensions.value &&
97 | additionalLabelOutput !== undefined &&
98 | dimensions.out.length > 1 && (
99 |
108 | )}
109 |
110 | );
111 | }
112 | }
113 |
114 | // PropTypes of this Class, containing the Global Layer Settings
115 | Edges.propTypes = {
116 | preferences: PropTypes.object.isRequired,
117 | layer_extreme_dimensions: PropTypes.object.isRequired,
118 | selection: PropTypes.array.isRequired,
119 | layer: PropTypes.object.isRequired,
120 | edges: PropTypes.array.isRequired,
121 | };
122 |
123 | // Map the State of the Application to the Props of this Class
124 | function mapStateToProps(state, ownProps) {
125 | return {
126 | preferences: state.preferences,
127 | layer_extreme_dimensions: state.layer_extreme_dimensions,
128 | selection: state.selection,
129 | };
130 | }
131 |
132 | // Map the Actions for the State to the Props of this Class
133 | function mapDispatchToProps(dispatch) {
134 | return { actions: bindActionCreators(actions, dispatch) };
135 | }
136 |
137 | export default connect(mapStateToProps, mapDispatchToProps)(Edges);
138 |
--------------------------------------------------------------------------------
/net2vis/src/components/network/FeaturesLabelComponent.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import * as paths from "../../paths";
5 |
6 | const FeaturesLabelComponent = ({
7 | features,
8 | x,
9 | layer_height,
10 | extreme_dimensions,
11 | layer,
12 | edges,
13 | }) => {
14 | const y_diff = [
15 | (extreme_dimensions.max_size - layer_height[0]) / 2,
16 | (extreme_dimensions.max_size - layer_height[1]) / 2,
17 | ]; // Calculate the vertical difference to center the Glyph
18 | const y_pos = [y_diff[1] + layer_height[1], y_diff[0] + layer_height[0]]; // Vertical Position of bottom-right and bottom-left Points
19 | if (layer.layer.properties.input.length > 1) {
20 | const position_reduced = paths.reducePosition(
21 | paths.getIncomingEdges(layer, edges)
22 | ); // Get the y positions of the straight parts of alloutgoing edges
23 | const y_off = Math.max(...position_reduced) - layer.y; // Calculate the Offset of the current Input Layer to this Layer
24 | y_pos[1] = y_pos[1] + y_off;
25 | } else if (layer.layer.properties.output.length > 1) {
26 | const position_reduced = paths.reducePosition(
27 | paths.getOutgoingEdges(layer, edges)
28 | ); // Get the y positions of the straight parts of alloutgoing edges
29 | const y_off = Math.max(...position_reduced) - layer.y; // Calculate the Offset of the current Input Layer to this Layer
30 | y_pos[0] = y_pos[0] + y_off;
31 | }
32 | var y = Math.max(y_pos[0], y_pos[1]);
33 | return (
34 |
35 | {features}
36 |
37 | );
38 | };
39 |
40 | // Proptypes of ToggleBurttons
41 | FeaturesLabelComponent.propTypes = {
42 | features: PropTypes.number.isRequired,
43 | x: PropTypes.number.isRequired,
44 | layer_height: PropTypes.array.isRequired,
45 | extreme_dimensions: PropTypes.object.isRequired,
46 | layer: PropTypes.object.isRequired,
47 | edges: PropTypes.array.isRequired,
48 | };
49 |
50 | export default FeaturesLabelComponent;
51 |
--------------------------------------------------------------------------------
/net2vis/src/components/network/NameLabelComponent.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import * as paths from "../../paths";
5 |
6 | const NameLabelComponent = ({
7 | name,
8 | x,
9 | layer_height,
10 | extreme_dimensions,
11 | layer,
12 | edges,
13 | features_above,
14 | }) => {
15 | const y_diff = [
16 | (extreme_dimensions.max_size - layer_height[0]) / 2,
17 | (extreme_dimensions.max_size - layer_height[1]) / 2,
18 | ]; // Calculate the vertical difference to center the Glyph
19 | let y_pos = [y_diff[1] + layer_height[1], y_diff[0] + layer_height[0]]; // Vertical Position of bottom-right and bottom-left Points
20 | if (layer.layer.properties.input.length > 1) {
21 | const position_reduced = paths.reducePosition(
22 | paths.getIncomingEdges(layer, edges)
23 | ); // Get the y positions of the straight parts of alloutgoing edges
24 | const y_off = Math.max(...position_reduced) - layer.y; // Calculate the Offset of the current Input Layer to this Layer
25 | y_pos[1] = y_pos[1] + y_off;
26 | } else if (layer.layer.properties.output.length > 1) {
27 | const position_reduced = paths.reducePosition(
28 | paths.getOutgoingEdges(layer, edges)
29 | ); // Get the y positions of the straight parts of alloutgoing edges
30 | const y_off = Math.max(...position_reduced) - layer.y; // Calculate the Offset of the current Input Layer to this Layer
31 | y_pos[0] = y_pos[0] + y_off;
32 | }
33 | let y = Math.max(y_pos[0], y_pos[1]);
34 | y = features_above ? y + 12 + 5 : y;
35 | return (
36 |
37 | {name}
38 |
39 | );
40 | };
41 |
42 | // Proptypes of ToggleBurttons
43 | NameLabelComponent.propTypes = {
44 | name: PropTypes.string.isRequired,
45 | x: PropTypes.number.isRequired,
46 | layer_height: PropTypes.array.isRequired,
47 | extreme_dimensions: PropTypes.object.isRequired,
48 | layer: PropTypes.object.isRequired,
49 | edges: PropTypes.array.isRequired,
50 | features_above: PropTypes.bool.isRequired,
51 | };
52 |
53 | export default NameLabelComponent;
54 |
--------------------------------------------------------------------------------
/net2vis/src/components/network/SampleComponent.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | const SampleComponent = ({
5 | extent,
6 | x,
7 | edge,
8 | layer_max_height,
9 | strokeWidth,
10 | }) => {
11 | var y_pos = edge.points[0].y; // Initialize the y_pos point Placeholder
12 | for (var j = 1; j < edge.points.length; j++) {
13 | // For all other Points
14 | if (y_pos === edge.points[j].y) {
15 | // The y_pos point had the same y value
16 | j = edge.points.length; // Break the Loop
17 | } else {
18 | // Not the same y value
19 | y_pos = edge.points[j].y; // Update the y_pos point Placeholder
20 | }
21 | }
22 | const transform = `translate(${x}, ${
23 | y_pos + layer_max_height / 2.0 - extent / 2.0
24 | })`; // Manipulate the position of the graph
25 | const style = {
26 | stroke: "lightgrey",
27 | strokeLinejoin: "round",
28 | strokeWidth: strokeWidth,
29 | fillOpacity: 0.0,
30 | strokeDasharray: 4,
31 | };
32 | return (
33 |
34 |
35 |
36 | );
37 | };
38 |
39 | // Proptypes of ToggleBurttons
40 | SampleComponent.propTypes = {
41 | extent: PropTypes.number.isRequired,
42 | x: PropTypes.number.isRequired,
43 | edge: PropTypes.object.isRequired,
44 | };
45 |
46 | export default SampleComponent;
47 |
--------------------------------------------------------------------------------
/net2vis/src/components/network/TooltipComponent.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { Tooltip } from "react-svg-tooltip";
4 |
5 | // ToggleButton Control Element appearance dependant on State of the Button. Action that is provided gets called on click.
6 | const TooltipComponent = ({
7 | properties_object,
8 | dimensions,
9 | tooltipRef,
10 | name,
11 | }) => {
12 | // Build the Properties Object for the Tooltip
13 | const buildProperties = (properties_object, dimensions) => {
14 | const keys = Object.keys(properties_object); // Get the Keys from the Object
15 | var properties = []; // Get all Properties
16 | for (var i in keys) {
17 | if (properties_object[keys[i]]) {
18 | properties.push({
19 | key: keys[i],
20 | prop: properties_object[keys[i]].toString(),
21 | });
22 | }
23 | }
24 | // Add Dimensions to Properties
25 | properties.push({ key: "Dimensions in", prop: dimensions.in.toString() });
26 | properties.push({ key: "Dimensions out", prop: dimensions.out.toString() });
27 | return properties;
28 | };
29 |
30 | const properties = buildProperties(properties_object, dimensions);
31 | return (
32 |
33 |
43 |
44 | Type: {name}
45 |
46 | {properties.map((pro, index) => (
47 |
48 | {pro.key}: {pro.prop}
49 |
50 | ))}
51 |
52 | );
53 | };
54 |
55 | // Proptypes of ToggleBurttons
56 | TooltipComponent.propTypes = {
57 | properties_object: PropTypes.object.isRequired,
58 | dimensions: PropTypes.object.isRequired,
59 | tooltipRef: PropTypes.object.isRequired,
60 | name: PropTypes.string.isRequired,
61 | };
62 |
63 | export default TooltipComponent;
64 |
--------------------------------------------------------------------------------
/net2vis/src/components/patterns/PatternComponent.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | // Component for displaying the code of the Neural Network implementation
4 | class Patterns extends React.Component {
5 | // Render the Code into the Code View if Toggled
6 | render() {
7 | return (
8 | // Editor with Syntax highlighting
9 |
10 |
18 |
19 |
20 |
28 |
29 |
30 |
31 |
39 |
40 |
41 |
50 |
51 |
52 |
53 |
61 |
62 |
63 |
71 |
76 |
77 |
85 |
90 |
91 |
99 |
104 |
105 |
113 |
114 |
115 |
123 |
124 |
125 |
133 |
134 |
135 |
143 |
144 |
145 |
146 | );
147 | }
148 | }
149 |
150 | export default Patterns;
151 |
--------------------------------------------------------------------------------
/net2vis/src/components/preferences/FeaturesComponent.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "react-redux";
3 | import PropTypes from "prop-types";
4 | import { bindActionCreators } from "redux";
5 |
6 | import InputField from "../input/InputField";
7 | import * as actions from "../../actions";
8 |
9 | class Features extends React.Component {
10 | // Feature Mapping Changes
11 | handleSelectChange = (e) => {
12 | var preferences = this.props.preferences;
13 | preferences.features_mapping.value = e.currentTarget.value;
14 | if (e.currentTarget.value !== "width") {
15 | preferences.layer_display_max_width = preferences.layer_display_min_width;
16 | }
17 | this.props.actions.updatePreferences(preferences, this.props.id);
18 | };
19 |
20 | // Width of a Layer Changes
21 | handleMinWidthChange = (e) => {
22 | var preferences = this.props.preferences;
23 | preferences.layer_display_min_width.value = parseInt(
24 | e.currentTarget.value,
25 | 10
26 | );
27 | this.props.actions.updatePreferences(preferences, this.props.id);
28 | };
29 |
30 | // Width of a Layer Changes
31 | handleMaxWidthChange = (e) => {
32 | var preferences = this.props.preferences;
33 | preferences.layer_display_max_width.value = parseInt(
34 | e.currentTarget.value,
35 | 10
36 | );
37 | this.props.actions.updatePreferences(preferences, this.props.id);
38 | };
39 |
40 | // Width of a Layer Changes
41 | handleWidthChange = (e) => {
42 | var preferences = this.props.preferences;
43 | preferences.layer_display_max_width.value = parseInt(
44 | e.currentTarget.value,
45 | 10
46 | );
47 | preferences.layer_display_min_width.value = parseInt(
48 | e.currentTarget.value,
49 | 10
50 | );
51 | this.props.actions.updatePreferences(preferences, this.props.id);
52 | };
53 |
54 | // Render the Preferences of the Visualization
55 | render() {
56 | return (
57 |
58 |
66 |
74 |
75 | );
76 | }
77 | }
78 |
79 | // Prop Types holding all the Preferences
80 | Features.propTypes = {
81 | id: PropTypes.string.isRequired,
82 | preferences: PropTypes.object.isRequired,
83 | };
84 |
85 | // Map the State to the Properties of this Component
86 | function mapStateToProps(state, ownProps) {
87 | return {
88 | id: state.id,
89 | preferences: state.preferences,
90 | };
91 | }
92 |
93 | // Map the actions of the State to the Props of this Class
94 | function mapDispatchToProps(dispatch) {
95 | return { actions: bindActionCreators(actions, dispatch) };
96 | }
97 |
98 | export default connect(mapStateToProps, mapDispatchToProps)(Features);
99 |
--------------------------------------------------------------------------------
/net2vis/src/components/preferences/PreferencesComponent.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "react-redux";
3 | import PropTypes from "prop-types";
4 |
5 | import NetworkPreferences from "./NetworkPreferencesComponent";
6 | import GroupPreferences from "./GroupPreferencesComponent";
7 | import LayerPreferences from "./LayerPreferencesComponent";
8 | import LegendPreferences from "./LegendPreferencesComponent";
9 |
10 | // Component for displaying the Preferences of the Visualization
11 | class Preferences extends React.Component {
12 | // Check if the currently selected Layer is a Group
13 | isInGroups = (selectedLayer) => {
14 | for (var i in this.props.groups) {
15 | if (selectedLayer === this.props.groups[i].name) {
16 | return this.props.groups[i];
17 | }
18 | }
19 | return null;
20 | };
21 |
22 | // Render the Preferences of the Visualization
23 | render() {
24 | if (this.props.preferences_toggle) {
25 | switch (this.props.preferences_mode) {
26 | case "network":
27 | return ;
28 | case "color":
29 | var group = this.isInGroups(this.props.selected_legend_item);
30 | if (group !== null) {
31 | return ;
32 | } else {
33 | return ;
34 | }
35 | case "legend":
36 | return ;
37 | default:
38 | return null;
39 | }
40 | } else {
41 | return null;
42 | }
43 | }
44 | }
45 |
46 | // Prop Types holding all the Preferences
47 | Preferences.propTypes = {
48 | preferences_mode: PropTypes.string.isRequired,
49 | preferences_toggle: PropTypes.bool.isRequired,
50 | selected_legend_item: PropTypes.string.isRequired,
51 | groups: PropTypes.array.isRequired,
52 | };
53 |
54 | // Map the State to the Properties of this Component
55 | function mapStateToProps(state, ownProps) {
56 | return {
57 | preferences_mode: state.preferences_mode,
58 | preferences_toggle: state.display.preferences_toggle,
59 | selected_legend_item: state.selected_legend_item,
60 | groups: state.groups,
61 | };
62 | }
63 |
64 | export default connect(mapStateToProps, undefined)(Preferences);
65 |
--------------------------------------------------------------------------------
/net2vis/src/graphs/index.js:
--------------------------------------------------------------------------------
1 | import * as dagre from 'dagre';
2 |
3 | // Build the network graph upon the Network representation
4 | export function buildGraphFromNetwork(
5 | network,
6 | layer_extreme_dimensions,
7 | preferences
8 | ) {
9 | var graph = new dagre.graphlib.Graph(); // Initialize the dagre Graph
10 | graph.setGraph({
11 | ranker: 'network-simplex',
12 | rankdir: 'LR',
13 | ranksep:
14 | preferences.layers_spacing_horizontal.value +
15 | preferences.stroke_width.value,
16 | nodesep: preferences.layers_spacing_vertical.value,
17 | }); // Set Graph Properties
18 | graph.setDefaultEdgeLabel(function () {
19 | return {};
20 | }); // Default Egde Label needs to be set
21 | for (var i in network.layers) {
22 | // Add all Layers to the Graph
23 | const layer = network.layers[i]; // Get the current Layer
24 | if (
25 | layer.properties.dimensions.in.length > 1 &&
26 | layer.properties.dimensions.out.length > 1
27 | ) {
28 | const channel_dim = preferences.channels_first.value
29 | ? 0
30 | : layer.properties.dimensions.out.length - 1;
31 | const spatial_dim = preferences.channels_first.value ? 1 : 0;
32 | const max_layer_dim = Math.max(
33 | layer.properties.dimensions.in[spatial_dim],
34 | layer.properties.dimensions.out[spatial_dim]
35 | ); // Get the maximum dimension of the layer (in vs out)
36 | var lay_diff =
37 | layer_extreme_dimensions.max_size - layer_extreme_dimensions.min_size; // Get the difference between Max and Min for the Extremes of the Layer
38 | lay_diff = lay_diff === 0 ? 1 : lay_diff; // Check if there is any difference in spatial resolution at all
39 | const dim_diff =
40 | preferences.layer_display_max_height.value -
41 | preferences.layer_display_min_height.value; // Get the difference between Max and Min for the Extremes of the Glyph Dimensions
42 | const perc =
43 | (max_layer_dim - layer_extreme_dimensions.min_size) / lay_diff; // Calculate the interpolation factor for boths sides of the Glyph
44 | var height = perc * dim_diff + preferences.layer_display_min_height.value; // Calculate the height for both sides of the Glyph
45 | height =
46 | height > preferences.layer_display_max_height.value
47 | ? preferences.layer_display_max_height.value
48 | : height; // Cap the height when something goes wrong
49 | var feat_diff =
50 | layer_extreme_dimensions.max_features -
51 | layer_extreme_dimensions.min_features; // Get the difference between max and min features of the Layers
52 | feat_diff = feat_diff === 0 ? 1 : feat_diff; // Check if there is any difference in features at all
53 | const fdim_diff =
54 | preferences.layer_display_max_width.value -
55 | preferences.layer_display_min_width.value; // Get the differnece between Max and Min width for the Glyph Dimensions
56 | const f_perc = (layer.properties.dimensions.out[channel_dim] - layer_extreme_dimensions.min_features) / feat_diff; // Calculate the interpolation factor
57 | const width =
58 | f_perc * fdim_diff + preferences.layer_display_min_width.value; // Calculate the width of the glyph
59 | graph.setNode(layer.id, { width: width, height: height, layer: layer }); // Add a Node to the Graph
60 | } else {
61 | graph.setNode(layer.id, {
62 | width: preferences.layer_display_min_width.value,
63 | height: preferences.layer_display_max_height.value,
64 | layer: layer,
65 | }); // Add a Node to the Graph
66 | }
67 | }
68 | for (var j in network.layers) {
69 | // Add all Edges to the Graph
70 | var layer_current = network.layers[j]; // Get the current Layer
71 | for (var k in layer_current.properties.output) {
72 | // Go over all outputs of the current Layer
73 | graph.setEdge(layer_current.id, layer_current.properties.output[k]); // Add the Edge to the Graph
74 | }
75 | }
76 | dagre.layout(graph); // Layout the graph to be displayed in a nice fashion
77 | return graph;
78 | }
79 |
--------------------------------------------------------------------------------
/net2vis/src/groups/Activation.js:
--------------------------------------------------------------------------------
1 | // Deactivates a Group and all Groups that depend on it
2 | export function deactivateGroup(selectedItem, groups) {
3 | var groupIndices = findDependingGroups(selectedItem, groups); // Get the indices for all groups to be deactivated
4 | for (var i in groupIndices) {
5 | // For all of these indices
6 | groups[groupIndices[i]].active = false; // Deactivate the group
7 | }
8 | }
9 |
10 | // Find and return all groups that depend on a given group
11 | export function findDependingGroups(selectedItem, groups) {
12 | var indices = findGroupDependenciesByName(selectedItem, groups); // Get all the indices of dependancy
13 | var unique = indices.filter(onlyUnique); // Remove duplicates from the array
14 | return unique;
15 | }
16 |
17 | // Find all Groups that depend on a given Group
18 | function findGroupDependenciesByName(selectedItem, groups) {
19 | var deps = []; // Initialize the dependencies Array
20 | for (var i = 0; i < groups.length; i++) {
21 | // For all Groups
22 | if (groups[i].name === selectedItem) {
23 | // If this is the given Group
24 | deps.push(i); // Add it to the Dependencies
25 | for (var j = 0; j < groups.length; j++) {
26 | // For all Groups
27 | for (var k = 0; k < groups[j].layers.length; k++) {
28 | // For all of ther Layers
29 | if (groups[i].name === groups[j].layers[k].name) {
30 | // If a layer has the same name as the Group that was given
31 | var recursion = findGroupDependenciesByName(groups[j].name, groups); // Recursively resolve their dependencies
32 | for (var l in recursion) {
33 | // For all recursive deps
34 | deps.push(recursion[l]); // Add them to the dependencies
35 | }
36 | }
37 | }
38 | }
39 | }
40 | }
41 | return deps;
42 | }
43 |
44 | // Activate a selected Group
45 | export function activateGroup(selectedItem, groups) {
46 | var groupIndices = findGroupDependents(selectedItem, groups); // Get the indices for all groups to be deactivated
47 | for (var i in groupIndices) {
48 | // For all of these indices
49 | groups[groupIndices[i]].active = true; // Deactivate the group
50 | }
51 | }
52 |
53 | // Find all Groups a given Group depends on
54 | function findGroupDependents(selectedItem, groups) {
55 | var indices = findGroupDependentsByName(selectedItem, groups); // Get all the indices of dependancy
56 | var unique = indices.filter(onlyUnique); // Remove duplicates from the array
57 | return unique;
58 | }
59 |
60 | // Find all groups, that the current group depends on
61 | function findGroupDependentsByName(selectedItem, groups) {
62 | var deps = []; // Initialize the dependents array
63 | for (var i = 0; i < groups.length; i++) {
64 | // For all Groups
65 | if (groups[i].name === selectedItem) {
66 | // If this is the current Group
67 | deps.push(i); // Add it to the Dependents
68 | for (var j = 0; j < groups[i].layers.length; j++) {
69 | // For all Layers of this Group
70 | for (var k = 0; k < groups.length; k++) {
71 | // For all Groups
72 | if (groups[k].name === groups[i].layers[j].name) {
73 | // If a Group has the same name as one of the Layers of the current Group
74 | var recursion = findGroupDependentsByName(groups[k].name, groups); // Recursively resolve their dependents
75 | for (var l in recursion) {
76 | // For all recursive deps
77 | deps.push(recursion[l]); // Add them to the dependencies
78 | }
79 | }
80 | }
81 | }
82 | return deps;
83 | }
84 | }
85 | }
86 |
87 | // Filter function to remove duplicates in the Indices List
88 | function onlyUnique(value, index, self) {
89 | return self.indexOf(value) === index; // Checks, if this the given value is at position index in the array
90 | }
91 |
--------------------------------------------------------------------------------
/net2vis/src/groups/Addition.js:
--------------------------------------------------------------------------------
1 | import * as occurences from "./Occurences";
2 | import * as concatenation from "./Concatenation";
3 |
4 | // A group was added to the Network, so the groups might need to also get grouped
5 | export function addGroup(groups, group) {
6 | for (var i in groups) {
7 | // Check each Group
8 | var groupOccur = occurences.findGroupOccurences(group, groups[i]); // If the new Group occurs in it
9 | for (var j in groupOccur) {
10 | // For all Occurences
11 | groups[i].layers = concatenation.concatenateLayers(
12 | groupOccur[j],
13 | groups[i],
14 | group
15 | ).layers; // Concatenate the Occurence
16 | }
17 | }
18 | groups.push(group); // Add the new Group to the existing ones
19 | }
20 |
--------------------------------------------------------------------------------
/net2vis/src/groups/Common.js:
--------------------------------------------------------------------------------
1 | // Find an input Node of a Group
2 | export function findInputNode(group) {
3 | for (var j in group.layers) {
4 | // Iterate over all layers in the Group
5 | if (group.layers[j].properties.input.length === 0) {
6 | // Layer has no inputs contained in the Group
7 | return { inputID: j, inputNode: group.layers[j] };
8 | }
9 | }
10 | }
11 |
12 | // Find an input Node of a Group
13 | export function findOutputNode(group) {
14 | for (var j in group.layers) {
15 | // Iterate over all layers in the Group
16 | if (group.layers[j].properties.output.length === 0) {
17 | // Layer has no inputs contained in the Group
18 | return { outputID: j, outputNode: group.layers[j] };
19 | }
20 | }
21 | }
22 |
23 | // Returning the maximum ID in the current network.
24 | export function maxID(network) {
25 | var id = 0; // Initialize the max ID
26 | for (var i in network.layers) {
27 | // Check all layers
28 | id = network.layers[i].id > id ? network.layers[i].id : id; // Set to bigger of current layer id and id
29 | }
30 | return id;
31 | }
32 |
--------------------------------------------------------------------------------
/net2vis/src/groups/Concatenation.js:
--------------------------------------------------------------------------------
1 | import * as common from "./Common";
2 | import * as layerCommon from "../layers/Common";
3 |
4 | // Replaces multiple Layers by a more abstract one.
5 | export function concatenateLayers(occurence, network, group) {
6 | var compressedNetwork = { layers: [] };
7 | if (layerIdsExist(occurence, network)) {
8 | // Check if all layers that should be concatenated still exist.
9 | var newID = common.maxID(network) + 1; // Get a new ID for the concatenation Layer
10 | var newLayer = {
11 | // Initialize the new layer
12 | id: newID,
13 | name: group.name,
14 | properties: {
15 | dimensions: {
16 | in: [100, 100, 100],
17 | out: [100, 100, 100],
18 | },
19 | input: [],
20 | output: [],
21 | properties: {},
22 | },
23 | };
24 | newLayer.properties.dimensions.in = getNewInputDimensions(
25 | occurence,
26 | network
27 | ); // Change the input Dimensions of the new Layer
28 | newLayer.properties.dimensions.out = getNewOutputDimensions(
29 | occurence,
30 | network
31 | ); // Change the output Dimensions of the new Layer
32 | for (var i in network.layers) {
33 | // Go over all Layers in the Network
34 | if (!checkInOccurence(occurence, network.layers[i].id)) {
35 | // Layer not in the compression List
36 | var layer = JSON.parse(JSON.stringify(network.layers[i])); // Copy layer from the original Network
37 | changeInputIfNeccessary(occurence, layer, newID, newLayer); // Change inputs of the layer if they are now missing
38 | changeOutputIfNeccessary(occurence, layer, newID, newLayer); // Change outputs of the layer if they are now missing
39 | compressedNetwork.layers.push(layer); // Add the layer to the compressed Network
40 | }
41 | }
42 | } else {
43 | return network;
44 | }
45 | compressedNetwork.layers.push(newLayer); // Add the new Layer to the compressed Network
46 | return compressedNetwork;
47 | }
48 |
49 | // Check if all layerIDs in the occurence still exist in the Network.
50 | function layerIdsExist(occurence, network) {
51 | for (var i in occurence) {
52 | // Check all items in the occurence
53 | if (!layerIdExists(occurence[i].matchID, network)) {
54 | // Layer does not exist
55 | return false;
56 | }
57 | }
58 | return true; // All layers exist
59 | }
60 |
61 | // Check if a Layer with a given ID exists.
62 | function layerIdExists(id, network) {
63 | for (var i in network.layers) {
64 | // Iterate over all layers in the Network
65 | if (network.layers[i].id === id) {
66 | // Check if it has the searched ID
67 | return true;
68 | }
69 | }
70 | return false; // No layer with the searched ID
71 | }
72 |
73 | // Check if a layer is in the occurence list
74 | function checkInOccurence(occurence, id) {
75 | for (var i in occurence) {
76 | // Iterate over the list
77 | if (occurence[i].matchID === id) {
78 | // Layer IDs match, in the list
79 | return true;
80 | }
81 | }
82 | return false; // Not in the List
83 | }
84 |
85 | // Change the input of a layer if the input to it is about to be removed.
86 | function changeInputIfNeccessary(occurence, layer, newID, newLayer) {
87 | for (var i in layer.properties.input) {
88 | // Iterate over all inputs of the layer
89 | for (var j in occurence) {
90 | // Iterate over all items in the occurence list
91 | if (layer.properties.input[i] === occurence[j].matchID) {
92 | // Input of the layer in the Occurence List
93 | layer.properties.input[i] = newID; // Set the input to the ID of the newly created abstract Layer
94 | newLayer.properties.output.push(layer.id); // Add the current layer to the outputs of the new Layer
95 | }
96 | }
97 | }
98 | }
99 |
100 | // Change the output of a layer if the output to it is about to be removed.
101 | function changeOutputIfNeccessary(occurence, layer, newID, newLayer) {
102 | for (var i in layer.properties.output) {
103 | // Iterate over all outputs of the layer
104 | for (var j in occurence) {
105 | // Iterate over all items in the occurence list
106 | if (layer.properties.output[i] === occurence[j].matchID) {
107 | // Output of the layer in the Occurence List
108 | layer.properties.output[i] = newID; // Set the input to the ID of the newly created abstract Layer
109 | newLayer.properties.input.push(layer.id); // Add the current layer to the inputs of the new Layer
110 | }
111 | }
112 | }
113 | }
114 |
115 | // Get the input dimensions for the new Layer
116 | function getNewInputDimensions(occurence, network) {
117 | for (var i in occurence) {
118 | // Iterate over the Occurence List
119 | if (occurence[i].properties.input.length === 0) {
120 | // No Inputs for a Layer
121 | var id = layerCommon.getLayerByID(occurence[i].matchID, network.layers);
122 | if (id >= 0) {
123 | // Layer has ID that occurence item matches
124 | return network.layers[id].properties.dimensions.in; // Return the input dimensions for this layer from the network
125 | }
126 | }
127 | }
128 | }
129 |
130 | // Get the output dimensions for the new Layer
131 | function getNewOutputDimensions(occurence, network) {
132 | for (var i in occurence) {
133 | // Iterate over the occurence List
134 | if (occurence[i].properties.output.length === 0) {
135 | // Not Outputs for a Layer
136 | var id = layerCommon.getLayerByID(occurence[i].matchID, network.layers);
137 | if (id >= 0) {
138 | // Layer has ID that occurence item matches
139 | return network.layers[id].properties.dimensions.out; // Return the input dimensions for this layer from the network
140 | }
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/net2vis/src/groups/Duplicates.js:
--------------------------------------------------------------------------------
1 | // Check if a group already exists
2 | export function groupDoesExist(group, groups) {
3 | for (var i in groups) {
4 | // For all Groups
5 | if (groupsEqualLayers(group, groups[i])) {
6 | // Check if the group is equal to the new group
7 | return true;
8 | }
9 | }
10 | return false;
11 | }
12 |
13 | // Chekch if two groups are equal
14 | function groupsEqualLayers(group1, group2) {
15 | if (group1.layers.length === group2.layers.length) {
16 | // Only possible if the groups have the same number of layers
17 | for (var i in group1.layers) {
18 | // For all layers in the Group
19 | if (group1.layers[i].name === group2.layers[i].name) {
20 | // Layers must have the same name
21 | if (
22 | group1.layers[i].properties.input.length !==
23 | group2.layers[i].properties.input.length
24 | ) {
25 | // Groups must have the same number of inputs
26 | return false;
27 | }
28 | if (
29 | group1.layers[i].properties.output.length !==
30 | group2.layers[i].properties.output.length
31 | ) {
32 | // Groups must have the same number of outputs
33 | return false;
34 | }
35 | } else {
36 | return false;
37 | }
38 | }
39 | } else {
40 | return false;
41 | }
42 | return true;
43 | }
44 |
--------------------------------------------------------------------------------
/net2vis/src/groups/Grouping.js:
--------------------------------------------------------------------------------
1 | import * as randomstring from "randomstring";
2 | import * as common from "../layers/Common";
3 |
4 | // Gouping a selection of layers
5 | export function groupLayers(network, selection) {
6 | if (checkGroupable(network, selection)) {
7 | // If the selection is groupable
8 | return generateGroup(network, selection); // Generate the Group
9 | } else {
10 | return undefined;
11 | }
12 | }
13 |
14 | // Check if the current selection is groupable
15 | function checkGroupable(network, selection) {
16 | if (selection.length < 2) {
17 | // Needs to contain more than one element
18 | return false;
19 | }
20 | var inNodes = 0;
21 | var outNodes = 0;
22 | for (var i in selection) {
23 | // Check groupability of each of the selected layers
24 | var layer =
25 | network.layers[common.getLayerByID(selection[i], network.layers)]; // Get the current layer
26 | var inputs = layer.properties.input; // Get the inputs of the current layer
27 | var outputs = layer.properties.output; // Get the outputs of the current layer
28 | var inContained = contained(inputs, selection); // Check how many of the inputs of the current layer are also selected.
29 | var outContained = contained(outputs, selection); // Check how many of the outputs of the current layer are also selected.
30 | if (inputs.length !== inContained && inContained > 0) {
31 | // Some, but not all inputs selected
32 | return false; // Not groupable
33 | } else if (outputs.length !== outContained && outContained > 0) {
34 | // Some, but not all outputs selected
35 | return false; // Not groupable
36 | } else if (outContained === 0 && inContained === 0) {
37 | // No in- or outputs at all selected
38 | return false; // Not Groupable
39 | }
40 | if (inContained === 0) {
41 | // No Inputs to this Layer
42 | inNodes = inNodes + 1; // Is input node, increase count
43 | }
44 | if (outContained === 0) {
45 | outNodes = outNodes + 1; // Is output node, increase count
46 | }
47 | }
48 | if (inNodes > 1 || outNodes > 1) {
49 | // Multiple input or output nodes
50 | return false; // Not groupable
51 | }
52 | return true; // More than one element selected, and all elements connected. Also no parallel path partly selected. Groupable!
53 | }
54 |
55 | // Check, how many of the given layers are contained in the selection
56 | function contained(layers, selection) {
57 | var contained = 0;
58 | for (var j in layers) {
59 | // Iterate over all layers
60 | if (selection.includes(layers[j])) {
61 | // If the selection includes the layer
62 | contained = contained + 1; // Increment the number of contained elements
63 | }
64 | }
65 | return contained;
66 | }
67 |
68 | // Generate the Group in the Graph
69 | function generateGroup(network, selection) {
70 | var group = {
71 | // Initialize the Group as empty object
72 | name: randomstring.generate(),
73 | active: true,
74 | layers: [],
75 | };
76 | for (var i in selection) {
77 | // Iterate over all selected Layers
78 | group.layers.push({
79 | id: network.layers[common.getLayerByID(selection[i], network.layers)].id,
80 | name:
81 | network.layers[common.getLayerByID(selection[i], network.layers)].name,
82 | properties: {
83 | dimensions: {
84 | in: [1, 1, 1],
85 | out: [1, 1, 1],
86 | },
87 | input: addInputsToLayer(selection, network, i),
88 | output: addOutputsToLayer(selection, network, i),
89 | properties: {},
90 | },
91 | }); // Add the Layers to the group
92 | }
93 | return group;
94 | }
95 |
96 | function addInputsToLayer(selection, network, i) {
97 | var layers = []; // Initialize input Layers to be added
98 | var inputs =
99 | network.layers[common.getLayerByID(selection[i], network.layers)].properties
100 | .input; // Get all inputs for the current Layer
101 | for (var j in inputs) {
102 | // Iterate over all inputs
103 | var inputLayer =
104 | network.layers[common.getLayerByID(inputs[j], network.layers)]; // Get the Layer of the current Input
105 | for (var k in selection) {
106 | // Iterate over all selected Items
107 | if (selection[k] === inputLayer.id) {
108 | // Current InputLayer is currently inspected selected Item
109 | layers.push(inputLayer.id); // Add the selection ID to the input layers
110 | }
111 | }
112 | }
113 | return layers;
114 | }
115 |
116 | function addOutputsToLayer(selection, network, i) {
117 | var layers = []; // Initialize output Layers to be added
118 | var outputs =
119 | network.layers[common.getLayerByID(selection[i], network.layers)].properties
120 | .output; // Get all outputs for the current Layer
121 | for (var j in outputs) {
122 | // Iterate over all outputs
123 | var outputLayer =
124 | network.layers[common.getLayerByID(outputs[j], network.layers)]; // Get the Layer of the current Output
125 | for (var k in selection) {
126 | // Iterate over all selected Items
127 | if (selection[k] === outputLayer.id) {
128 | // Current OutputLayer id currently inspected selected Item
129 | layers.push(outputLayer.id); // Add the selection ID to the input layers
130 | }
131 | }
132 | }
133 | return layers;
134 | }
135 |
--------------------------------------------------------------------------------
/net2vis/src/groups/Removal.js:
--------------------------------------------------------------------------------
1 | import * as common from "./Common";
2 | import * as layerCommon from "../layers/Common";
3 |
4 | // Deletes a selected Group
5 | export function deleteGroup(selectedItem, groups) {
6 | var deletionGroup = getGroupByName(selectedItem, groups); // Get the Group that is to be deleted
7 | groups.splice(deletionGroup.id, 1); // Remove the Group
8 | expandSuperGroups(deletionGroup.group, groups); // Expand Groups that depend on that Group
9 | }
10 |
11 | // Expand Groups that depend on a given group
12 | function expandSuperGroups(group, groups) {
13 | for (var i in groups) {
14 | // For all Groups
15 | for (var j = 0; j < groups[i].layers.length; j++) {
16 | // And all their layers
17 | if (groups[i].layers[j].name === group.name) {
18 | // If the layer is the one to be expanded
19 | expandLayer(groups[i], j, group); // Expand the Layer
20 | j = 0; // Need to go back to be sure not to skip Layers
21 | }
22 | }
23 | }
24 | }
25 |
26 | // Expand a Layer that depends on a Group
27 | function expandLayer(expandGroup, position, expansionGroup) {
28 | var input = common.findInputNode(expansionGroup); // Get the inputNode of the group to be inserted into the other
29 | var output = common.findOutputNode(expansionGroup); // Get the outputNode of the group to be inserted into the other
30 | var maxID = common.maxID(expandGroup) + 1; // Get the maximum ID of the group to always be bigger
31 | for (var i in expansionGroup.layers) {
32 | // Iterate over all layers in the group to be inserted
33 | var newLayer = JSON.parse(JSON.stringify(expansionGroup.layers[i])); // Generate a copy of the current layer
34 | newLayer.id = newLayer.id + maxID; // Set a new and unused ID for the current layer
35 | if (i === input.inputID) {
36 | // If it is the input to the group that expands the other
37 | newLayer.properties.input = expandGroup.layers[position].properties.input; // The input to this layer is the same as the input of the expanded layer
38 | for (var j in expandGroup.layers[position].properties.input) {
39 | // For all of these inputs
40 | var currentPreOutput =
41 | expandGroup.layers[
42 | layerCommon.getLayerByID(
43 | expandGroup.layers[position].properties.input[j],
44 | expandGroup.layers
45 | )
46 | ].properties.output; // Get the outputs
47 | for (var k in currentPreOutput) {
48 | // Iterate over the outputs
49 | if (currentPreOutput[k] === expandGroup.layers[position].id) {
50 | // If the output matches the id of the layer to be expanded
51 | currentPreOutput[k] = newLayer.id; // Set the output to be the new ID
52 | }
53 | }
54 | }
55 | for (var l in newLayer.properties.output) {
56 | // For all outputs of this layer
57 | newLayer.properties.output[l] = newLayer.properties.output[l] + maxID; // The current output gets updated to reflect the new IDs of the group that expands the layer
58 | }
59 | } else if (i === output.outputID) {
60 | // If it is the input to the group that expands the other
61 | newLayer.properties.output =
62 | expandGroup.layers[position].properties.output; // the putput to this layer is the same as the output of the expanded Layer
63 | for (var m in expandGroup.layers[position].properties.output) {
64 | // Foe all of these outputs
65 | var currentPreInput =
66 | expandGroup.layers[
67 | layerCommon.getLayerByID(
68 | expandGroup.layers[position].properties.output[m],
69 | expandGroup.layers
70 | )
71 | ].properties.input; // Get the Inputs
72 | for (var n in currentPreInput) {
73 | // Iterate over the Inputs
74 | if (currentPreInput[n] === expandGroup.layers[position].id) {
75 | // If the input matches the id of the Layer to be expanded
76 | currentPreInput[n] = newLayer.id; // Set teh input to be the new ID
77 | }
78 | }
79 | }
80 | for (var o in newLayer.properties.input) {
81 | // For all inputs of this Layer
82 | newLayer.properties.input[o] = newLayer.properties.input[o] + maxID; // The current input gets updated to reflect the new IDs of the group that expands the layer
83 | }
84 | } else {
85 | // Neither in nor output of the group that expands the layer
86 | for (var p in newLayer.properties.output) {
87 | // For all outputs
88 | newLayer.properties.output[p] = newLayer.properties.output[p] + maxID; // The current output gets updated to reflect the new IDs of the group that expands the Layer
89 | }
90 | for (var q in newLayer.properties.input) {
91 | // Fro all Inputs
92 | newLayer.properties.input[q] = newLayer.properties.input[q] + maxID; // The current input gets updated to reflect the new IDs of the group that expands the Layer
93 | }
94 | }
95 | expandGroup.layers.push(newLayer); // Add the new layer
96 | }
97 | expandGroup.layers.splice(position, 1); // Remove the expanded Layer
98 | }
99 |
100 | // Get a group by its name
101 | export function getGroupByName(name, groups) {
102 | for (var i in groups) {
103 | // For all group
104 | if (name === groups[i].name) {
105 | // If the names match
106 | return { id: i, group: groups[i] }; // Return the group
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/net2vis/src/groups/Sort.js:
--------------------------------------------------------------------------------
1 | // Sort the Groups based on their dependancies
2 | export function sortGroups(groups, legendItems) {
3 | for (var i = 0; i < groups.length - 1; i++) {
4 | // Go over all groups
5 | if (groupMustGoToEnd(groups, i)) {
6 | // Check if the group should be moved to the end
7 | moveGroupToEnd(groups, i, legendItems); // Move the Group to the end
8 | i = i - 1; // Do not skip a group
9 | }
10 | }
11 | }
12 |
13 | // Check if a Group has to go to the end of the list
14 | function groupMustGoToEnd(groups, i) {
15 | for (var j = i + 1; j < groups.length; j++) {
16 | // For all groups after this group
17 | for (var k in groups[i].layers) {
18 | // For all the layers of the original Group
19 | if (groups[i].layers[k].name === groups[j].name) {
20 | // If the layer is the currently inspected Group
21 | return true; // The Group that was provided needs to be moved
22 | }
23 | }
24 | }
25 | }
26 |
27 | // Move a Group and the Legend Item to the End
28 | function moveGroupToEnd(groups, i, legendItems) {
29 | for (var j in legendItems) {
30 | // For all Legend Items
31 | if (j === groups[i].name) {
32 | // If it is the searched one
33 | var temp = legendItems[j]; // Save it
34 | delete legendItems[j]; // Delete it from the List
35 | legendItems[j] = temp; // Add it to the List
36 | }
37 | }
38 | var moved = groups.splice(i, 1); // Get the Group to be moved out of the Groups list
39 | groups = groups.push(moved[0]); // Add it back in at the end
40 | }
41 |
--------------------------------------------------------------------------------
/net2vis/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 |
4 | import App from "./App";
5 | import registerServiceWorker from "./registerServiceWorker";
6 |
7 | import "./styles/index.css";
8 |
9 | // Render the Application into the html 'root' element
10 | ReactDOM.render(, document.getElementById("root"));
11 | registerServiceWorker();
12 |
--------------------------------------------------------------------------------
/net2vis/src/layers/Common.js:
--------------------------------------------------------------------------------
1 | // Returns the layer index gien an ID
2 | export function getLayerByID(id, layers) {
3 | for (var i in layers) {
4 | // Iterate over all layers
5 | if (layers[i].id === id) {
6 | // Layer ID matches
7 | return i;
8 | }
9 | }
10 | return -1; // No Layer with this ID
11 | }
12 |
13 | // Returning the maximum ID in the current network.
14 | export function maxID(network) {
15 | var id = 0; // Initialize the max ID
16 | for (var i in network.layers) {
17 | // Check all layers
18 | id = network.layers[i].id > id ? network.layers[i].id : id; // Set to bigger of current layer id and id
19 | }
20 | return id;
21 | }
22 |
--------------------------------------------------------------------------------
/net2vis/src/layers/Hiding.js:
--------------------------------------------------------------------------------
1 | import * as common from "./Common";
2 |
3 | export function hideLayers(network, layerTypes) {
4 | var net = network;
5 | for (var key in layerTypes) {
6 | var layerType = layerTypes[key];
7 | if (layerType.hidden) {
8 | hideLayersByType(key, net);
9 | }
10 | }
11 | return net;
12 | }
13 |
14 | function hideLayersByType(key, network) {
15 | var layer = 0;
16 | while (layer < network.layers.length) {
17 | if (key === network.layers[layer].name) {
18 | var inputs = network.layers[layer].properties.input;
19 | var outputs = network.layers[layer].properties.output;
20 | for (var input in inputs) {
21 | var outOfIn =
22 | network.layers[common.getLayerByID(inputs[input], network.layers)]
23 | .properties.output;
24 | for (var outIn in outOfIn) {
25 | if (outOfIn[outIn] === network.layers[layer].id) {
26 | outOfIn.splice(outIn, 1);
27 | }
28 | }
29 | for (var newOut in outputs) {
30 | outOfIn.push(outputs[newOut]);
31 | }
32 | }
33 | for (var output in outputs) {
34 | var inOfOut =
35 | network.layers[common.getLayerByID(outputs[output], network.layers)]
36 | .properties.input;
37 | for (var inOut in inOfOut) {
38 | if (inOfOut[inOut] === network.layers[layer].id) {
39 | inOfOut.splice(inOut, 1);
40 | }
41 | }
42 | for (var newIn in inputs) {
43 | inOfOut.push(inputs[newIn]);
44 | }
45 | }
46 | network.layers.splice(layer, 1);
47 | } else {
48 | layer = layer + 1;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/net2vis/src/layers/Splitting.js:
--------------------------------------------------------------------------------
1 | import * as common from './Common';
2 |
3 | export function addSplitLayers(network) {
4 | var net = network;
5 | replaceMultiOutLayers(net);
6 | return net;
7 | }
8 |
9 | function replaceMultiOutLayers(network) {
10 | for (var layer in network.layers) {
11 | var outputs = network.layers[layer].properties.output;
12 | if (outputs.length > 1) {
13 | var newID = common.maxID(network) + 1;
14 | var newLayer = {
15 | id: newID,
16 | name: 'Split',
17 | properties: {
18 | dimensions: {
19 | in: network.layers[layer].properties.dimensions.out,
20 | out: network.layers[layer].properties.dimensions.out,
21 | },
22 | input: [network.layers[layer].id],
23 | output: outputs,
24 | properties: {},
25 | },
26 | };
27 | network.layers[layer].properties.output = [newID];
28 | for (var output in outputs) {
29 | var inputs =
30 | network.layers[common.getLayerByID(outputs[output], network.layers)]
31 | .properties.input;
32 | for (var input in inputs) {
33 | if (inputs[input] === network.layers[layer].id) {
34 | inputs[input] = newID;
35 | }
36 | }
37 | }
38 | network.layers.push(newLayer);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/net2vis/src/media/download_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/viscom-ulm/Net2Vis/a37935eda001ff89a7ef1d355d2ce76d3fb5fa02/net2vis/src/media/download_icon.png
--------------------------------------------------------------------------------
/net2vis/src/media/group_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/viscom-ulm/Net2Vis/a37935eda001ff89a7ef1d355d2ce76d3fb5fa02/net2vis/src/media/group_icon.png
--------------------------------------------------------------------------------
/net2vis/src/reducers/AlertSnackReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function alertSnackReducer(
5 | state = initialState.alert_snack,
6 | action
7 | ) {
8 | switch (action.type) {
9 | case types.UPDATE_ALERT_SNACK_SUCCESS:
10 | return action.alertSnack;
11 | default:
12 | return state;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/CodeReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function codeReducer(state = initialState.code, action) {
5 | switch (action.type) {
6 | case types.LOAD_CODE_SUCCESS:
7 | return action.code;
8 | case types.UPDATE_CODE_SUCESS:
9 | return action.code;
10 | default:
11 | return state;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/ColorModeReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function colorModeReducer(
5 | state = initialState.color_mode,
6 | action
7 | ) {
8 | switch (action.type) {
9 | case types.SET_SELECTION_COLOR_MODE:
10 | return { selection: action.mode, generation: state.generation };
11 | case types.SET_GENERATION_COLOR_MODE:
12 | return { selection: state.selection, generation: action.mode };
13 | default:
14 | return state;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/CompressionReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 | import * as occurences from "../groups/Occurences";
4 | import * as concatenate from "../groups/Concatenation";
5 | import * as hiding from "../layers/Hiding";
6 |
7 | export default function compressionReducer(
8 | state = initialState.compressed_network,
9 | action
10 | ) {
11 | switch (action.type) {
12 | case types.INITIALIZE_COMPRESSED_NETWORK:
13 | var net = JSON.parse(JSON.stringify(action.network));
14 | net = hiding.hideLayers(net, action.layerTypes);
15 | for (var i in action.groups) {
16 | if (action.groups[i].active) {
17 | var occur = occurences.findGroupOccurences(action.groups[i], net); // Check, where this group can be found
18 | for (var j in occur) {
19 | net = concatenate.concatenateLayers(
20 | occur[j],
21 | net,
22 | action.groups[i]
23 | );
24 | }
25 | }
26 | }
27 | return net;
28 | default:
29 | return state;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/DisplayReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function displayReducer(state = initialState.display, action) {
5 | switch (action.type) {
6 | case types.TOGGLE_CODE:
7 | return { ...state, code_toggle: !state.code_toggle };
8 | case types.TOGGLE_PREFERENCES:
9 | return { ...state, preferences_toggle: !state.preferences_toggle };
10 | case types.TOGGLE_LEGEND:
11 | return { ...state, legend_toggle: !state.legend_toggle };
12 | case types.TOGGLE_ALERT:
13 | return { ...state, alert_toggle: !state.alert_toggle };
14 | case types.TOGGLE_HELP:
15 | return { ...state, help_toggle: !state.help_toggle };
16 | case types.TOGGLE_UPLOAD:
17 | return { ...state, upload_toggle: !state.upload_toggle };
18 | default:
19 | return state;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/ErrorReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function errorReducer(state = initialState.error, action) {
5 | switch (action.type) {
6 | case types.ADD_ERROR:
7 | return action.data;
8 | case types.REMOVE_ERROR:
9 | return {};
10 | default:
11 | return state;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/GroupsReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function groupsReducer(state = initialState.groups, action) {
5 | switch (action.type) {
6 | case types.LOAD_GROUPS_SUCCESS:
7 | return action.groups;
8 | case types.UPDATE_GROUPS:
9 | return action.groups;
10 | default:
11 | return state;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/IDReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function idReducer(state = initialState.id, action) {
5 | switch (action.type) {
6 | case types.SET_ID:
7 | return action.id;
8 | default:
9 | return state;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/LayerExtremeDimensionsReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function layerExtremeDimensionsReducer(
5 | state = initialState.layer_extreme_dimensions,
6 | action
7 | ) {
8 | switch (action.type) {
9 | case types.SET_LAYERS_EXTREMES:
10 | var max = 0,
11 | min = Infinity;
12 | var max_f = 0,
13 | min_f = Infinity;
14 | var max_d = 0,
15 | min_d = Infinity;
16 | const layers = action.network.layers;
17 | for (var i in layers) {
18 | const dimensions = layers[i].properties.dimensions;
19 | if (dimensions.out.length > 1) {
20 | for (let j = 0; j < dimensions.out.length - 1; j++) {
21 | max = dimensions.out[j] > max ? dimensions.out[j] : max;
22 | min = dimensions.out[j] < min ? dimensions.out[j] : min;
23 | }
24 | max_f =
25 | dimensions.out[dimensions.out.length - 1] > max_f
26 | ? dimensions.out[dimensions.out.length - 1]
27 | : max_f;
28 | min_f =
29 | dimensions.out[dimensions.out.length - 1] < min_f
30 | ? dimensions.out[dimensions.out.length - 1]
31 | : min_f;
32 | } else if (dimensions.in.length === 1) {
33 | const dimensions = layers[i].properties.dimensions;
34 | max_d = dimensions.out[0] > max_d ? dimensions.out[0] : max_d;
35 | min_d = dimensions.out[0] < min_d ? dimensions.out[0] : min_d;
36 | }
37 | if (dimensions.in.length > 1) {
38 | for (let j = 0; j < dimensions.in.length - 1; j++) {
39 | max = dimensions.in[j] > max ? dimensions.in[j] : max;
40 | min = dimensions.in[j] < min ? dimensions.in[j] : min;
41 | }
42 | }
43 | }
44 | return {
45 | max_size: max,
46 | min_size: min,
47 | max_features: max_f,
48 | min_features: min_f,
49 | max_dense: max_d,
50 | min_dense: min_d,
51 | };
52 | default:
53 | return state;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/LayerTypesSettingsReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 | import * as colors from "../colors";
4 |
5 | export default function layerTypesSettignsReducer(
6 | state = initialState.layer_types_settings,
7 | action
8 | ) {
9 | switch (action.type) {
10 | case types.LOAD_LAYER_TYPES_SUCCESS:
11 | if (action.network === undefined) {
12 | return action.layerTypes;
13 | }
14 | var lTypes = action.layerTypes; // Get the layer types
15 | for (const layer of action.network.layers) {
16 | // For all layers
17 | if (lTypes[layer.name] === undefined) {
18 | // If the layer is not yet in layertypes
19 | lTypes[layer.name] = {
20 | // Add the layer type
21 | color: colors.generateNewColor(lTypes, action.generationMode), // Set the color
22 | alias: layer.name, // Set the name
23 | texture: colors.generateNewTexture(lTypes), // Set the fallback texture
24 | hidden: false,
25 | dense: layer.properties.dimensions.out.length === 1,
26 | };
27 | }
28 | }
29 | return lTypes;
30 | default:
31 | return state;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/LegendBboxReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function legendBboxReducer(
5 | state = initialState.legend_bbox,
6 | action
7 | ) {
8 | switch (action.type) {
9 | case types.SET_LEGEND_BBOX:
10 | return action.bbox;
11 | default:
12 | return state;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/LegendPreferencesReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function legendPreferencesReducer(
5 | state = initialState.legend_preferences,
6 | action
7 | ) {
8 | switch (action.type) {
9 | case types.UPDATE_LEGEND_PREFERENCES_SUCCESS:
10 | return action.legend_preferences;
11 | case types.LOAD_LEGEND_PREFERENCES_SUCCESS:
12 | return action.legend_preferences;
13 | default:
14 | return state;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/LegendTransformReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function legendTransformReducer(
5 | state = initialState.legend_transform,
6 | action
7 | ) {
8 | switch (action.type) {
9 | case types.MOVE_LEGEND:
10 | var x = state.x - action.group_displacement[0];
11 | var y = state.y - action.group_displacement[1];
12 | return { x: x, y: y, scale: state.scale };
13 | case types.ZOOM_LEGEND: // Zoming the Legend Graph
14 | var factor = 1.2;
15 | if (action.legend_zoom > 0) {
16 | // Zooming out
17 | factor = 1.0 / (action.legend_zoom / factor); // Ensure, that the factor is of appropriate Size
18 | } else if (action.legend_zoom < 0) {
19 | // Zooming in
20 | factor = -action.legend_zoom / 1.1 / factor; // Ensure, that the factor is of appropriate Size
21 | }
22 | var scale = state.scale * factor; // Multiply the previous scale with the current factor
23 | return { x: state.x, y: state.y, scale: scale };
24 | default:
25 | return state;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/NetworkBboxReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function networkBboxReducer(
5 | state = initialState.network_bbox,
6 | action
7 | ) {
8 | switch (action.type) {
9 | case types.SET_NETWORK_BBOX:
10 | return action.bbox;
11 | default:
12 | return state;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/NetworkReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function networkReducer(state = initialState.network, action) {
5 | switch (action.type) {
6 | case types.LOAD_NETWORK_SUCCESS:
7 | return action.network;
8 | default:
9 | return state;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/PreferencesModeReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function preferencesModeReducer(
5 | state = initialState.preferences_mode,
6 | action
7 | ) {
8 | switch (action.type) {
9 | case types.SET_PREFERENCE_MODE:
10 | return action.name;
11 | default:
12 | return state;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/PreferencesReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function preferencesReducer(
5 | state = initialState.preferences,
6 | action
7 | ) {
8 | switch (action.type) {
9 | case types.UPDATE_PREFERENCES_SUCCESS:
10 | return action.preferences;
11 | case types.LOAD_PREFERENCES_SUCCESS:
12 | let prefs = action.preferences;
13 | if (prefs.show_name === undefined) {
14 | prefs.show_name = {
15 | value: false,
16 | type: "switch",
17 | description: "Name Label",
18 | };
19 | }
20 | if (prefs.channels_first === undefined) {
21 | prefs.channels_first = {
22 | value: false,
23 | type: "switch",
24 | description: "Channels First",
25 | };
26 | }
27 | return prefs;
28 | default:
29 | return state;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/SelectedLegendItemReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function selectedLegendItemReducer(
5 | state = initialState.selected_legend_item,
6 | action
7 | ) {
8 | switch (action.type) {
9 | case types.SET_SELECTED_LEGEND_ITEM:
10 | return action.name;
11 | default:
12 | return state;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/SelectionReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function selectionReducer(
5 | state = initialState.selection,
6 | action
7 | ) {
8 | switch (action.type) {
9 | case types.SELECT_LAYER: // One new Layer To Select
10 | return state.concat(action.id);
11 | case types.SELECT_LAYERS: // Multiple New Layers to select
12 | var toSelect = [];
13 | for (var i in action.layers) {
14 | toSelect.push(action.layers[i].id);
15 | }
16 | return toSelect;
17 | case types.DESELECT_LAYER: // One Layer to deselect
18 | return state.filter((item) => item !== action.id);
19 | case types.DESELECT_LAYERS: // Deselect all Layers
20 | return [];
21 | default:
22 | return state;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/TransformReducer.js:
--------------------------------------------------------------------------------
1 | import initialState from "./initialState";
2 | import * as types from "../actions/types";
3 |
4 | export default function transformReducer(
5 | state = initialState.group_transform,
6 | action
7 | ) {
8 | switch (action.type) {
9 | case types.MOVE_GROUP:
10 | var x = state.x - action.group_displacement[0];
11 | var y = state.y - action.group_displacement[1];
12 | return { x: x, y: y, scale: state.scale };
13 | case types.ZOOM_GROUP: // Zoming the Network Graph
14 | let factor = 1.2;
15 | if (action.group_zoom > 0) {
16 | // Zooming out
17 | factor = 1.0 / (action.group_zoom / factor); // Ensure, that the factor is of appropriate Size
18 | } else if (action.group_zoom < 0) {
19 | // Zooming in
20 | factor = -action.group_zoom / 1.1 / factor; // Ensure, that the factor is of appropriate Size
21 | }
22 | var scale = state.scale * factor; // Multiply the previous scale with the current factor
23 | return { x: state.x, y: state.y, scale: scale };
24 | default:
25 | return state;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | // Import all Reducers
2 | import { combineReducers } from "redux";
3 | import id from "./IDReducer";
4 | import network from "./NetworkReducer";
5 | import code from "./CodeReducer";
6 | import group_transform from "./TransformReducer";
7 | import legend_transform from "./LegendTransformReducer";
8 | import display from "./DisplayReducer";
9 | import layer_types_settings from "./LayerTypesSettingsReducer";
10 | import preferences from "./PreferencesReducer";
11 | import error from "./ErrorReducer";
12 | import layer_extreme_dimensions from "./LayerExtremeDimensionsReducer";
13 | import selection from "./SelectionReducer";
14 | import preferences_mode from "./PreferencesModeReducer";
15 | import selected_legend_item from "./SelectedLegendItemReducer";
16 | import groups from "./GroupsReducer";
17 | import compressed_network from "./CompressionReducer";
18 | import legend_preferences from "./LegendPreferencesReducer";
19 | import color_mode from "./ColorModeReducer";
20 | import network_bbox from "./NetworkBboxReducer";
21 | import legend_bbox from "./LegendBboxReducer";
22 | import alert_snack from "./AlertSnackReducer";
23 |
24 | // Combine all Reducers
25 | export default combineReducers({
26 | id,
27 | network,
28 | code,
29 | group_transform,
30 | legend_transform,
31 | display,
32 | layer_types_settings,
33 | preferences,
34 | error,
35 | layer_extreme_dimensions,
36 | selection,
37 | preferences_mode,
38 | selected_legend_item,
39 | groups,
40 | compressed_network,
41 | legend_preferences,
42 | color_mode,
43 | network_bbox,
44 | legend_bbox,
45 | alert_snack,
46 | });
47 |
--------------------------------------------------------------------------------
/net2vis/src/reducers/initialState.js:
--------------------------------------------------------------------------------
1 | // Set the initial State of the Application
2 | export default {
3 | id: "",
4 | network: {},
5 | code: "",
6 | group_transform: {
7 | x: 0,
8 | y: 0,
9 | scale: 1,
10 | },
11 | legend_transform: {
12 | x: 0,
13 | y: 0,
14 | scale: 1,
15 | },
16 | display: {
17 | code_toggle: true,
18 | preferences_toggle: true,
19 | legend_toggle: true,
20 | alert_toggle: false,
21 | help_toggle: false,
22 | upload_toggle: false,
23 | },
24 | layer_types_settings: {},
25 | preferences: {
26 | layer_display_min_height: {
27 | value: 30,
28 | type: "number",
29 | description: "Minimum Layer Height",
30 | },
31 | layer_display_max_height: {
32 | value: 150,
33 | type: "number",
34 | description: "Maximum Layer Height",
35 | },
36 | layer_display_min_width: {
37 | value: 20,
38 | type: "number",
39 | description: "Minimum width of Layers",
40 | },
41 | layer_display_max_width: {
42 | value: 80,
43 | type: "number",
44 | description: "Maximum width of Layers",
45 | },
46 | layers_spacing_horizontal: {
47 | value: 0,
48 | type: "number",
49 | description: "Horizontal spacing between Layers",
50 | },
51 | layers_spacing_vertical: {
52 | value: 0,
53 | type: "number",
54 | description: "Vertical spacing between Layers",
55 | },
56 | features_mapping: {
57 | value: "none",
58 | type: "choice",
59 | description: "Visual mapping of the Features",
60 | },
61 | show_dimensions: {
62 | value: true,
63 | type: "switch",
64 | description: "Dimensions Label",
65 | },
66 | show_features: {
67 | value: true,
68 | type: "switch",
69 | description: "Features Label",
70 | },
71 | show_name: {
72 | value: false,
73 | type: "switch",
74 | description: "Name Label",
75 | },
76 | add_splitting: {
77 | value: false,
78 | type: "switch",
79 | description: "Replace Split Layers",
80 | },
81 | show_samples: {
82 | value: true,
83 | type: "switch",
84 | description: "Input/Output Samples",
85 | },
86 | channels_first: {
87 | value: false,
88 | type: "switch",
89 | description: "Channels First",
90 | },
91 | no_colors: { value: false, type: "switch", description: "Disable Colors" },
92 | stroke_width: { value: 4, type: "number", description: "Stroke Width" },
93 | },
94 | layer_extreme_dimensions: {
95 | max_size: 2,
96 | min_size: 1,
97 | max_features: 2,
98 | min_features: 1,
99 | max_dense: 2,
100 | min_dense: 1,
101 | },
102 | error: {},
103 | selection: [],
104 | preferences_mode: "network",
105 | selected_legend_item: "",
106 | groups: [],
107 | compressed_network: {},
108 | legend_preferences: {
109 | element_spacing: {
110 | value: 70,
111 | type: "number",
112 | description: "Spacing between Elements",
113 | },
114 | layer_height: { value: 30, type: "number", description: "Layer Height" },
115 | layer_width: { value: 10, type: "number", description: "Layer Width" },
116 | layers_spacing_horizontal: {
117 | value: 5,
118 | type: "number",
119 | description: "Horizontal spacing between Layers",
120 | },
121 | layers_spacing_vertical: {
122 | value: 10,
123 | type: "number",
124 | description: "Vertical spacing between Layers",
125 | },
126 | complex_spacing: {
127 | value: 15,
128 | type: "number",
129 | description: "Spacing before complex Layer",
130 | },
131 | stroke_width: { value: 2, type: "number", description: "Stroke Width" },
132 | reverse_order: {
133 | value: false,
134 | type: "switch",
135 | description: "Reverse Legend Order",
136 | },
137 | },
138 | color_mode: {
139 | selection: "Palette",
140 | generation: "Palette",
141 | },
142 | network_bbox: {},
143 | legend_bbox: {},
144 | alert_snack: {
145 | open: false,
146 | message: "",
147 | },
148 | };
149 |
--------------------------------------------------------------------------------
/net2vis/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === "localhost" ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === "[::1]" ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18 | )
19 | );
20 |
21 | export default function register() {
22 | if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
23 | // The URL constructor is available in all browsers that support SW.
24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25 | if (publicUrl.origin !== window.location.origin) {
26 | // Our service worker won't work if PUBLIC_URL is on a different origin
27 | // from what our page is served on. This might happen if a CDN is used to
28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29 | return;
30 | }
31 |
32 | window.addEventListener("load", () => {
33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34 |
35 | if (isLocalhost) {
36 | // This is running on localhost. Lets check if a service worker still exists or not.
37 | checkValidServiceWorker(swUrl);
38 |
39 | // Add some additional logging to localhost, pointing developers to the
40 | // service worker/PWA documentation.
41 | navigator.serviceWorker.ready.then(() => {
42 | console.log(
43 | "This web app is being served cache-first by a service " +
44 | "worker. To learn more, visit https://goo.gl/SC7cgQ"
45 | );
46 | });
47 | } else {
48 | // Is not local host. Just register service worker
49 | registerValidSW(swUrl);
50 | }
51 | });
52 | }
53 | }
54 |
55 | function registerValidSW(swUrl) {
56 | navigator.serviceWorker
57 | .register(swUrl)
58 | .then((registration) => {
59 | registration.onupdatefound = () => {
60 | const installingWorker = registration.installing;
61 | installingWorker.onstatechange = () => {
62 | if (installingWorker.state === "installed") {
63 | if (navigator.serviceWorker.controller) {
64 | // At this point, the old content will have been purged and
65 | // the fresh content will have been added to the cache.
66 | // It's the perfect time to display a "New content is
67 | // available; please refresh." message in your web app.
68 | console.log("New content is available; please refresh.");
69 | } else {
70 | // At this point, everything has been precached.
71 | // It's the perfect time to display a
72 | // "Content is cached for offline use." message.
73 | console.log("Content is cached for offline use.");
74 | }
75 | }
76 | };
77 | };
78 | })
79 | .catch((error) => {
80 | console.error("Error during service worker registration:", error);
81 | });
82 | }
83 |
84 | function checkValidServiceWorker(swUrl) {
85 | // Check if the service worker can be found. If it can't reload the page.
86 | fetch(swUrl)
87 | .then((response) => {
88 | // Ensure service worker exists, and that we really are getting a JS file.
89 | if (
90 | response.status === 404 ||
91 | response.headers.get("content-type").indexOf("javascript") === -1
92 | ) {
93 | // No service worker found. Probably a different app. Reload the page.
94 | navigator.serviceWorker.ready.then((registration) => {
95 | registration.unregister().then(() => {
96 | window.location.reload();
97 | });
98 | });
99 | } else {
100 | // Service worker found. Proceed as normal.
101 | registerValidSW(swUrl);
102 | }
103 | })
104 | .catch(() => {
105 | console.log(
106 | "No internet connection found. App is running in offline mode."
107 | );
108 | });
109 | }
110 |
111 | export function unregister() {
112 | if ("serviceWorker" in navigator) {
113 | navigator.serviceWorker.ready.then((registration) => {
114 | registration.unregister();
115 | });
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/net2vis/src/selection/index.js:
--------------------------------------------------------------------------------
1 | import * as common from "../layers/Common";
2 |
3 | // Get all paths from a node s to node d in the network
4 | export function allPaths(s, d, network) {
5 | var paths = []; // Initially, no Paths found
6 | traverseWay(s, d, network, [], paths); // Traverse the way from s on
7 | return paths;
8 | }
9 |
10 | // Traverse the Network from node u on
11 | function traverseWay(u, d, network, path, paths) {
12 | path.push(u);
13 | if (u.id === d.id) {
14 | // If u is the destination
15 | paths.push(path); // Add the path to the pathes
16 | } else if (u.properties.output.length === 0) {
17 | // If an output node has been reached
18 | paths.push(path); // Add the uncomplete path to the pathes
19 | } else {
20 | // Way goes on
21 | for (var i = 0; i < u.properties.output.length; i++) {
22 | // For all Outputs of the current node
23 | if (i === 0) {
24 | // For the first of all outputs
25 | var nextLayer =
26 | network.layers[
27 | common.getLayerByID(u.properties.output[i], network.layers)
28 | ]; // Get the next Layer
29 | traverseWay(nextLayer, d, network, path, paths); // Go further along the way
30 | } else {
31 | // Not the first output of the current node
32 | var newPath = JSON.parse(JSON.stringify(path)); // Copy the path, since a new path has been found
33 | var nextWay =
34 | network.layers[
35 | common.getLayerByID(u.properties.output[i], network.layers)
36 | ]; // Get the next Layer along this way
37 | traverseWay(nextWay, d, network, newPath, paths); // Go along this alternate way
38 | }
39 | }
40 | }
41 | }
42 |
43 | // Reduce the paths to just every layer they contain once
44 | export function reducePaths(paths) {
45 | var items = []; // No Layers initially
46 | for (var i in paths) {
47 | // For all Paths
48 | for (var j in paths[i]) {
49 | // For all their Layers
50 | var present = false; // Placeholder for if the Layer is already in the Layers variable
51 | for (var k in items) {
52 | // For all Layers in the Layers Variable
53 | if (items[k].id === paths[i][j].id) {
54 | // If the currently inspected Layer is the current one in the Layers variable
55 | present = true; // Layer already present
56 | }
57 | }
58 | if (!present) {
59 | // If Layer not present
60 | items.push(paths[i][j]); // Add it to the Layers
61 | }
62 | }
63 | }
64 | return items;
65 | }
66 |
67 | // Check multi inputs for if all their inputs are contained in the paths
68 | export function checkMultiInput(paths) {
69 | for (var i in paths) {
70 | // For all Paths
71 | for (var j in paths[i]) {
72 | // For all Layers in these Paths
73 | if (paths[i][j].properties.input.length > 1) {
74 | // If it has more than one Input
75 | for (var k in paths[i][j].properties.input) {
76 | // For all these Inputs
77 | var inPaths = false; // Placeholder for if the input is contained in the paths
78 | for (var l in paths) {
79 | // For all paths
80 | for (var m in paths[l]) {
81 | // For all Layers in these Paths
82 | if (paths[l][m].id === paths[i][j].properties.input[k]) {
83 | // Check if the current Layer ID matches the inspected Input
84 | inPaths = true; // Set the Placeholder to true
85 | }
86 | }
87 | }
88 | if (!inPaths) {
89 | // If the input is not in the paths
90 | return false;
91 | }
92 | }
93 | }
94 | }
95 | }
96 | return true;
97 | }
98 |
--------------------------------------------------------------------------------
/net2vis/src/setupProxy.js:
--------------------------------------------------------------------------------
1 | const { createProxyMiddleware } = require("http-proxy-middleware");
2 |
3 | module.exports = function (app) {
4 | app.use(createProxyMiddleware("/api", { target: "http://localhost:5000" }));
5 | };
6 |
--------------------------------------------------------------------------------
/net2vis/src/styles/index.scss:
--------------------------------------------------------------------------------
1 | // General Properties
2 | html,
3 | body {
4 | height: 100%;
5 | overflow: hidden;
6 | font-family: Roboto, sans-serif;
7 | }
8 |
9 | // Header only Properties (For Menu Bar)
10 | header {
11 | position: fixed;
12 | top: 0;
13 | left: 0;
14 | right: 0;
15 | color: white;
16 | background-color: #a32638;
17 |
18 | .wrapper {
19 | display: flex;
20 | justify-content: space-between;
21 | width: 100%;
22 | margin-left: 50px;
23 | }
24 |
25 | .menu {
26 | display: flex;
27 | flex-direction: row;
28 | }
29 |
30 | .menuitem {
31 | display: flex;
32 | flex-direction: row;
33 | height: calc(100% - 2px);
34 | border-bottom: 2px solid rgba(0, 0, 0, 0);
35 | padding-left: 12px;
36 | padding-right: 12px;
37 | }
38 |
39 | .menuselect {
40 | padding-left: 24px;
41 | padding-right: 24px;
42 | display: flex;
43 | align-items: center;
44 | }
45 |
46 | .menuitem:hover {
47 | background-color: #1b80de;
48 | text-decoration: none;
49 | cursor: pointer;
50 | }
51 |
52 | div {
53 | display: block;
54 | color: white;
55 | text-align: center;
56 | line-height: 62px;
57 | }
58 |
59 | div a:hover {
60 | background-color: rgb(141, 47, 61);
61 | text-decoration: none;
62 | cursor: pointer;
63 | }
64 |
65 | .selected {
66 | border-bottom: 2px solid #ffffff;
67 | }
68 |
69 | img {
70 | display: block;
71 | margin: auto;
72 | height: 30px;
73 | }
74 | }
75 |
76 | .preferencesWrapper {
77 | display: flex;
78 | height: 100%;
79 | flex-direction: column;
80 | justify-content: space-between;
81 | }
82 |
83 | .innerPreferencesWrapper {
84 | display: flex;
85 | flex-direction: column;
86 | overflow: auto;
87 | }
88 |
89 | // Some Content Helpers
90 | .content {
91 | padding: 70px 0px 0px 0px;
92 | height: 100%;
93 | background-color: #f5f5f5;
94 | overflow: hidden;
95 | }
96 |
97 | .full {
98 | width: 100%;
99 | height: 100%;
100 | }
101 |
102 | .codePaper {
103 | width: 100%;
104 | height: calc(100% - 8px);
105 | }
106 |
107 | #codeDiv {
108 | height: calc(100% - 48px);
109 | }
110 |
111 | .codeItem {
112 | padding-bottom: 8px;
113 | padding-right: 10px;
114 | padding-left: 10px;
115 | flex: 1 1 0%;
116 | }
117 |
118 | // Different Areas
119 | #ColorPicker {
120 | padding: 10px;
121 | }
122 |
123 | // Grids for the Areas of the Application
124 | .mainGrid {
125 | height: 100%;
126 | }
127 |
128 | .codeGrid {
129 | height: 100%;
130 | width: 400px;
131 | }
132 |
133 | .svgGrid {
134 | height: calc(100% - 300px);
135 | width: 100%;
136 | }
137 |
138 | .preferencesGrid {
139 | height: 100%;
140 | width: 300px;
141 | }
142 |
143 | .legendGrid {
144 | height: 300px;
145 | width: 100%;
146 | }
147 |
148 | // Classes for Styling of individual Elements
149 | .noselect {
150 | -webkit-touch-callout: none; /* iOS Safari */
151 | -webkit-user-select: none; /* Safari */
152 | -khtml-user-select: none; /* Konqueror HTML */
153 | -moz-user-select: none; /* Firefox */
154 | -ms-user-select: none; /* Internet Explorer/Edge */
155 | user-select: none; /* Non-prefixed version, currently supported by Chrome and Opera */
156 | }
157 |
158 | .preferenceItem {
159 | padding-right: 10px;
160 | }
161 |
162 | .inputElement {
163 | width: calc(100%);
164 | }
165 |
166 | .formselect {
167 | margin-top: 16px;
168 | margin-bottom: 8px;
169 | }
170 |
171 | .paddedButton {
172 | padding-bottom: 8px;
173 | padding-right: 10px;
174 | }
175 |
176 | .legendItem {
177 | width: 200px;
178 | height: 50px;
179 | margin: 10px;
180 | line-height: 50px;
181 | text-align: center;
182 | color: black;
183 | }
184 |
185 | .reflex-size-aware {
186 | overflow: hidden;
187 | }
188 |
189 | .ace_scrollbar {
190 | display: none !important;
191 | }
192 |
193 | .ace_editor {
194 | background: rgba(0, 0, 0, 0) !important;
195 | }
196 |
197 | .ace_gutter {
198 | background: #f5f5f5 !important;
199 | z-index: auto !important;
200 | }
201 |
202 | svg text {
203 | cursor: default;
204 | -webkit-user-select: none;
205 | -moz-user-select: none;
206 | -ms-user-select: none;
207 | user-select: none;
208 | }
209 |
210 | #loader {
211 | width: 100%;
212 | height: 100%;
213 | top: 0;
214 | left: 0;
215 | display: none;
216 | position: fixed;
217 | background-color: rgba($color: black, $alpha: 0.3);
218 | justify-content: center;
219 | align-items: center;
220 | }
221 |
222 | .spinner {
223 | border: 16px solid #f3f3f3; /* Light grey */
224 | border-top: 16px solid #2196f3; /* Blue */
225 | border-radius: 50%;
226 | width: 120px;
227 | height: 120px;
228 | animation: spin 2s linear infinite;
229 | }
230 |
231 | @keyframes spin {
232 | 0% {
233 | transform: rotate(0deg);
234 | }
235 | 100% {
236 | transform: rotate(360deg);
237 | }
238 | }
239 |
240 | .updateButtonContainer {
241 | display: flex;
242 | }
243 |
244 | .dropzoneContainer {
245 | border: #f3f3f3;
246 | border-style: dashed;
247 | padding: 5px;
248 | border-radius: 4px;
249 | }
250 |
--------------------------------------------------------------------------------
/net2vis_teaser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/viscom-ulm/Net2Vis/a37935eda001ff89a7ef1d355d2ce76d3fb5fa02/net2vis_teaser.png
--------------------------------------------------------------------------------
/net2vis_teaser_legend.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/viscom-ulm/Net2Vis/a37935eda001ff89a7ef1d355d2ce76d3fb5fa02/net2vis_teaser_legend.png
--------------------------------------------------------------------------------