From bb943cce9cb391bc59f2df08e040cf72a8c68e07 Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Mon, 18 Sep 2017 09:26:21 +0200 Subject: [PATCH] Improve plot widgets Add y-axis scale options Add time option to table-plot --- .../dialog/edit-widget-control-creator.js | 7 ++- src/components/widget-factory.js | 14 +++-- src/components/widget-gauge.js | 2 - src/components/widget-plot-table.js | 4 +- src/components/widget-plot.js | 60 +++++++++++------- src/components/widget-plot/plot-legend.js | 26 ++++---- src/components/widget-plot/plot.js | 62 ++++++++++++------- 7 files changed, 107 insertions(+), 68 deletions(-) diff --git a/src/components/dialog/edit-widget-control-creator.js b/src/components/dialog/edit-widget-control-creator.js index 8846079..9cafc98 100644 --- a/src/components/dialog/edit-widget-control-creator.js +++ b/src/components/dialog/edit-widget-control-creator.js @@ -60,7 +60,8 @@ export default function createControls(widgetType = null, widget = null, session validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />, validateForm(id)} simulation={simulation} handleChange={(e) => plotBoundOnChange(e)} />, validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />, - handleChange(e)} /> + handleChange(e)} />, + handleChange(e)} /> ); break; case 'Table': @@ -94,7 +95,9 @@ export default function createControls(widgetType = null, widget = null, session dialogControls.push( validateForm(id)} simulation={simulation} handleChange={(e) => plotTableBoundOnChange(e)} />, validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />, - handleChange(e)} /> + handleChange(e)} />, + validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />, + handleChange(e)} /> ); break; case 'Slider': diff --git a/src/components/widget-factory.js b/src/components/widget-factory.js index 010c01f..faed0a3 100644 --- a/src/components/widget-factory.js +++ b/src/components/widget-factory.js @@ -47,6 +47,9 @@ class WidgetFactory { widget.minHeight = 200; widget.width = 400; widget.height = 200; + widget.yMin = 0; + widget.yMax = 10; + widget.yUseMinMax = false; break; case 'Table': widget.simulator = defaultSimulator; @@ -68,11 +71,14 @@ class WidgetFactory { widget.preselectedSignals = []; widget.signals = []; // initialize selected signals widget.ylabel = ''; - widget.minWidth = 400; - widget.minHeight = 300; - widget.width = 500; - widget.height = 500; + widget.minWidth = 200; + widget.minHeight = 100; + widget.width = 600; + widget.height = 300; widget.time = 60; + widget.yMin = 0; + widget.yMax = 10; + widget.yUseMinMax = false; break; case 'Image': widget.minWidth = 20; diff --git a/src/components/widget-gauge.js b/src/components/widget-gauge.js index 984b382..9a5cc4b 100644 --- a/src/components/widget-gauge.js +++ b/src/components/widget-gauge.js @@ -127,8 +127,6 @@ class WidgetGauge extends Component { zones = zones.map(zone => { return Object.assign({}, zone, { min: (zone.min * step) + +minValue, max: zone.max * step + +minValue, strokeStyle: '#' + zone.strokeStyle }); }); - - console.log(zones); } this.gauge.setOptions({ diff --git a/src/components/widget-plot-table.js b/src/components/widget-plot-table.js index a998d09..0cef2f5 100644 --- a/src/components/widget-plot-table.js +++ b/src/components/widget-plot-table.js @@ -37,7 +37,6 @@ class WidgetPlotTable extends Component { } componentWillReceiveProps(nextProps) { - // Update internal selected signals state with props (different array objects) if (this.props.widget.signals !== nextProps.widget.signals) { this.setState( {signals: nextProps.widget.signals}); @@ -158,6 +157,9 @@ class WidgetPlotTable extends Component { time={this.props.widget.time} width={this.props.widget.width - 100} height={this.props.widget.height - 55} + yMin={this.props.widget.yMin} + yMax={this.props.widget.yMax} + yUseMinMax={this.props.widget.yUseMinMax} /> diff --git a/src/components/widget-plot.js b/src/components/widget-plot.js index 8333e37..2c51e0d 100644 --- a/src/components/widget-plot.js +++ b/src/components/widget-plot.js @@ -25,45 +25,59 @@ import Plot from './widget-plot/plot'; import PlotLegend from './widget-plot/plot-legend'; class WidgetPlot extends React.Component { - render() { - const simulator = this.props.widget.simulator; - const simulation = this.props.simulation; - let legendSignals = []; - let simulatorData = []; + constructor(props) { + super(props); + + this.state = { + data: [], + legend: [] + }; + } + + componentWillReceiveProps(nextProps) { + const simulator = nextProps.widget.simulator; + const simulation = nextProps.simulation; // Proceed if a simulation with models and a simulator are available - if (simulator && this.props.data[simulator.node] != null && this.props.data[simulator.node][simulator.simulator] != null && simulation && simulation.models.length > 0) { + if (simulator && nextProps.data[simulator.node] != null && nextProps.data[simulator.node][simulator.simulator] != null && simulation && simulation.models.length > 0) { const model = simulation.models.find(model => model.simulator.node === simulator.node && model.simulator.simulator === simulator.simulator); - const chosenSignals = this.props.widget.signals; + const chosenSignals = nextProps.widget.signals; - simulatorData = this.props.data[simulator.node][simulator.simulator].values.filter((values, index) => ( - this.props.widget.signals.findIndex(value => value === index) !== -1 + const data = nextProps.data[simulator.node][simulator.simulator].values.filter((values, index) => ( + nextProps.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) => { + const legend = model.mapping.reduce( (accum, model_signal, signal_index) => { if (chosenSignals.includes(signal_index)) { accum.push({ index: signal_index, name: model_signal.name }); } return accum; }, []); + + this.setState({ data, legend }); + } else { + this.setState({ data: [], legend: [] }); } + } - return ( -
-
- -
- - + render() { + return
+
+
- ); + + +
; } } diff --git a/src/components/widget-plot/plot-legend.js b/src/components/widget-plot/plot-legend.js index 3771a5c..fcaeae0 100644 --- a/src/components/widget-plot/plot-legend.js +++ b/src/components/widget-plot/plot-legend.js @@ -7,25 +7,21 @@ * Unauthorized copying of this file, via any medium is strictly prohibited. **********************************************************************************/ -import React, { Component } from 'react'; +import React from 'react'; import { scaleOrdinal, schemeCategory10 } from 'd3-scale'; -class PlotLegend extends Component { - // constructor(props) { - // super(props); - // } - +class PlotLegend extends React.Component { render() { - var colorScale = scaleOrdinal(schemeCategory10); + const colorScale = scaleOrdinal(schemeCategory10); - return ( -
- { this.props.signals.map( (signal) => -
   {signal.name}
) - } -
- ); + return
+ {this.props.signals.map(signal => +
+   {signal.name} +
+ )} +
; } } -export default PlotLegend; \ No newline at end of file +export default PlotLegend; diff --git a/src/components/widget-plot/plot.js b/src/components/widget-plot/plot.js index 8af08f4..9be58fa 100644 --- a/src/components/widget-plot/plot.js +++ b/src/components/widget-plot/plot.js @@ -22,22 +22,38 @@ class Plot extends React.Component { constructor(props) { super(props); + // create dummy axes + const xScale = scaleTime().domain([Date.now(), Date.now() + 5 * 1000]).range([leftMargin, props.width]); + const yScale = scaleLinear().domain([0, 10]).range([props.height, bottomMargin]); + + const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(date => timeFormat("%M:%S")(date)); + const yAxis = axisLeft().scale(yScale).ticks(5); + this.state = { - data: null + data: null, + xAxis, + yAxis }; } componentWillReceiveProps(nextProps) { // check if data is valid if (nextProps.data == null || nextProps.data.length === 0 || nextProps.data[0].length === 0) { - this.setState({ data: null }); + // create empty plot axes + const xScale = scaleTime().domain([Date.now(), Date.now() + 5 * 1000]).range([leftMargin, nextProps.width]); + const yScale = scaleLinear().domain([0, 10]).range([nextProps.height, bottomMargin]); + + const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(date => timeFormat("%M:%S")(date)); + const yAxis = axisLeft().scale(yScale).ticks(5); + + this.setState({ data: null, xAxis, yAxis }); return; } // only show data in requested time let data = nextProps.data; - const firstTimestamp = data[0][data[0].length - 1].x - this.props.time * 1000; + const firstTimestamp = data[0][data[0].length - 1].x - nextProps.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); @@ -50,20 +66,26 @@ class Plot extends React.Component { xRange[0] = xRange[1] - nextProps.time * 1000; } - let yRange = [0, 0]; + let yRange; - 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]; + if (nextProps.yUseMinMax) { + yRange = [nextProps.yMin, nextProps.yMax]; + } else { + yRange = [0, 0]; - return values; - }); + 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); @@ -77,18 +99,16 @@ class Plot extends React.Component { } render() { - if (this.state.data == null) return false; + console.log(this.state); - return( - - select(node).call(this.state.xAxis)} style={{ transform: `translateY(${this.props.height}px)` }} /> - select(node).call(this.state.yAxis)} style={{ transform: `translateX(${leftMargin}px)`}} /> + return + select(node).call(this.state.xAxis)} style={{ transform: `translateY(${this.props.height}px)` }} /> + select(node).call(this.state.yAxis)} style={{ transform: `translateX(${leftMargin}px)`}} /> - - {this.state.data} - - - ); + + {this.state.data} + + ; } }