diff --git a/package.json b/package.json
index 98e3fa3..57c7657 100644
--- a/package.json
+++ b/package.json
@@ -5,13 +5,17 @@
"dependencies": {
"bootstrap": "^3.3.7",
"classnames": "^2.2.5",
- "d3-scale": "^1.0.5",
+ "d3-array": "^1.2.0",
+ "d3-axis": "^1.0.8",
+ "d3-scale": "^1.0.6",
+ "d3-selection": "^1.1.0",
+ "d3-shape": "^1.2.0",
+ "d3-time-format": "^2.0.5",
"es6-promise": "^4.0.5",
"flux": "^3.1.2",
"gaugeJS": "^1.3.2",
"immutable": "^3.8.1",
"rc-slider": "^7.0.1",
- "rd3": "^0.7.4",
"react": "^15.4.2",
"react-bootstrap": "^0.31.1",
"react-contextmenu": "^2.3.0",
diff --git a/src/components/widget-plot-table.js b/src/components/widget-plot-table.js
index 7862a05..c674773 100644
--- a/src/components/widget-plot-table.js
+++ b/src/components/widget-plot-table.js
@@ -108,7 +108,9 @@ class WidgetPlotTable extends Component {
let simulatorData = [];
if (this.props.data[simulator.node] != null && this.props.data[simulator.node][simulator.simulator] != null) {
- simulatorData = this.props.data[simulator.node][simulator.simulator];
+ simulatorData = this.props.data[simulator.node][simulator.simulator].values.filter((values, index) => (
+ this.props.widget.signals.findIndex(value => value === index) !== -1
+ ));
}
if (this.state.preselectedSignals && this.state.preselectedSignals.length > 0) {
@@ -153,7 +155,12 @@ class WidgetPlotTable extends Component {
diff --git a/src/components/widget-plot.js b/src/components/widget-plot.js
index c02aa97..fb89a07 100644
--- a/src/components/widget-plot.js
+++ b/src/components/widget-plot.js
@@ -19,13 +19,12 @@
* along with VILLASweb. If not, see .
******************************************************************************/
-import React, { Component } from 'react';
+import React from 'react';
import Plot from './widget-plot/plot';
import PlotLegend from './widget-plot/plot-legend';
-class WidgetPlot extends Component {
-
+class WidgetPlot extends React.Component {
render() {
const simulator = this.props.widget.simulator;
const simulation = this.props.simulation;
@@ -38,15 +37,17 @@ class WidgetPlot extends Component {
const model = simulation.models.find( model => model.simulator.node === simulator.node && model.simulator.simulator === simulator.simulator );
const chosenSignals = this.props.widget.signals;
- simulatorData = this.props.data[simulator.node][simulator.simulator];
+ simulatorData = this.props.data[simulator.node][simulator.simulator].values.filter((values, index) => (
+ this.props.widget.signals.findIndex(value => value === index) !== -1
+ ));
// Query the signals that will be displayed in the legend
legendSignals = model.mapping.reduce( (accum, model_signal, signal_index) => {
- if (chosenSignals.includes(signal_index)) {
- accum.push({ index: signal_index, name: model_signal.name });
- }
- return accum;
- }, []);
+ if (chosenSignals.includes(signal_index)) {
+ accum.push({ index: signal_index, name: model_signal.name });
+ }
+ return accum;
+ }, []);
}
return (
@@ -54,8 +55,14 @@ class WidgetPlot extends Component {
{this.props.widget.name}
+
);
diff --git a/src/components/widget-plot/plot.js b/src/components/widget-plot/plot.js
index 7b0c3aa..8af08f4 100644
--- a/src/components/widget-plot/plot.js
+++ b/src/components/widget-plot/plot.js
@@ -7,135 +7,89 @@
* Unauthorized copying of this file, via any medium is strictly prohibited.
**********************************************************************************/
-import React, { Component } from 'react';
-import { LineChart } from 'rd3';
-import { scaleOrdinal, schemeCategory10 } from 'd3-scale';
+import React from 'react';
+import { scaleLinear, scaleTime, scaleOrdinal, schemeCategory10 } from 'd3-scale';
+import { extent } from 'd3-array';
+import { line } from 'd3-shape';
+import { axisBottom, axisLeft } from 'd3-axis';
+import { select } from 'd3-selection';
+import { timeFormat } from 'd3-time-format';
-class Plot extends Component {
+const leftMargin = 30;
+const bottomMargin = 20;
+
+class Plot extends React.Component {
constructor(props) {
super(props);
- this.chartWrapper = null;
-
- // Initialize plot size and data
- this.state = Object.assign(
- { size: { w: 0, h: 0 } },
- this.getPlotInitData(true)
- );
- }
-
- // Get an object with 'invisible' init data for the last minute.
- // Include start/end timestamps if required.
- getPlotInitData(withRangeTimestamps = false) {
-
- const initSecondTime = Date.now();
- const initFirstTime = initSecondTime - 1000 * 60; // Decrease 1 min
- const values = [{ values: [{x: initFirstTime, y: 0}], strokeWidth: 0 }];
-
- let output = withRangeTimestamps?
- { sequence: 0, values: values, firstTimestamp: initFirstTime, latestTimestamp: initSecondTime, } :
- { sequence: 0, values: values };
-
- return output;
+ this.state = {
+ data: null
+ };
}
componentWillReceiveProps(nextProps) {
- let nextData = nextProps.simulatorData;
-
- // handle plot size
- const w = this.chartWrapper.offsetWidth - 20;
- const h = this.chartWrapper.offsetHeight - 20;
- const currentSize = this.state.size;
- if (w !== currentSize.w || h !== currentSize.h) {
- this.setState({size: { w, h } });
+ // check if data is valid
+ if (nextProps.data == null || nextProps.data.length === 0 || nextProps.data[0].length === 0) {
+ this.setState({ data: null });
+ return;
}
- // If signals were cleared, clear the plot (triggers a new state)
- if (this.signalsWereJustCleared(nextProps)) { this.clearPlot(); return; }
-
- // If no signals have been selected, just leave
- if (nextProps.signals == null || nextProps.signals.length === 0) { return; }
-
- // Identify simulation reset
- if (nextData == null || nextData.length === 0 || nextData.values[0].length === 0) { this.clearPlot(); return; }
-
- // check if new data, otherwise skip
- if (this.state.sequence >= nextData.sequence) { return; }
-
- this.updatePlotData(nextProps);
-
- }
-
- signalsWereJustCleared(nextProps) {
-
- return this.props.signals &&
- nextProps.signals &&
- this.props.signals.length > 0 &&
- nextProps.signals.length === 0;
- }
-
- clearPlot() {
- this.setState( this.getPlotInitData(false) );
- }
-
- updatePlotData(nextProps) {
- let nextData = nextProps.simulatorData;
-
- // get timestamps
- var latestTimestamp = nextData.values[0][nextData.values[0].length - 1].x;
- var firstTimestamp = latestTimestamp - nextProps.time * 1000;
- var firstIndex;
-
- if (nextData.values[0][0].x < firstTimestamp) {
- // find element index representing firstTimestamp
- firstIndex = nextData.values[0].findIndex((value) => {
- return value.x >= firstTimestamp;
- });
- } else {
- firstIndex = 0;
- firstTimestamp = nextData.values[0][0].x;
- latestTimestamp = firstTimestamp + nextProps.time * 1000;
+ // only show data in requested time
+ let data = nextProps.data;
+
+ const firstTimestamp = data[0][data[0].length - 1].x - this.props.time * 1000;
+ if (data[0][0].x < firstTimestamp) {
+ // only show data in range (+100 ms)
+ const index = data[0].findIndex(value => value.x >= firstTimestamp - 100);
+ data = data.map(values => values.slice(index));
}
- // copy all values for each signal in time region
- var values = [];
- nextProps.signals.forEach((signal_index, i, arr) => (
- // Include signal index, useful to relate them to the signal selection
- values.push(
- {
- index: signal_index,
- values: nextData.values[signal_index].slice(firstIndex, nextData.values[signal_index].length - 1)})
- ));
+ // calculate paths for data
+ let xRange = extent(data[0], p => new Date(p.x));
+ if (xRange[1] - xRange[0] < nextProps.time * 1000) {
+ xRange[0] = xRange[1] - nextProps.time * 1000;
+ }
- this.setState({ values: values, firstTimestamp: firstTimestamp, latestTimestamp: latestTimestamp, sequence: nextData.sequence });
+ let yRange = [0, 0];
+
+ data.map(values => {
+ const range = extent(values, p => p.y);
+ if (range[0] < yRange[0]) yRange[0] = range[0];
+ if (range[1] > yRange[1]) yRange[1] = range[1];
+
+ return values;
+ });
+
+ // create scale functions for both axes
+ const xScale = scaleTime().domain(xRange).range([leftMargin, nextProps.width]);
+ const yScale = scaleLinear().domain(yRange).range([nextProps.height, bottomMargin]);
+
+ const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(date => timeFormat("%M:%S")(date));
+ const yAxis = axisLeft().scale(yScale).ticks(5);
+
+ // generate paths from data
+ const sparkLine = line().x(p => xScale(p.x)).y(p => yScale(p.y));
+ const lineColor = scaleOrdinal(schemeCategory10);
+
+ const lines = data.map((values, index) => );
+
+ this.setState({ data: lines, xAxis, yAxis });
}
render() {
- // Make tick count proportional to the plot width using a rough scale ratio
- var tickCount = Math.round(this.state.size.w / 80);
+ if (this.state.data == null) return false;
- return (
- this.chartWrapper = domNode }>
- {this.state.sequence != null &&
- { if (d != null) { return new Date(d.x); } }}
- xAxisTickCount={ tickCount }
- yAxisLabel={ this.props.yAxisLabel }
- hoverAnimation={false}
- circleRadius={0}
- domain={{ x: [this.state.firstTimestamp, this.state.latestTimestamp] }}
- />
- }
-
+ return(
+
);
}
-
}
export default Plot;