2019-08-06 17:23:40 +02:00

215 lines
5.8 KiB
JavaScript

import React from 'react'
import { Line, Bar } from 'react-chartjs-2'
import debounceRender from 'react-debounce-render'
import platform from 'platform'
import { downloadAsJson } from '../../../lib/json-download/index.js'
import { datasetNames } from '../data/index.js'
import { datasetTemplates, genOptions, beautifyLabels } from './chartOptions.js'
export class Chart extends React.Component {
render() {
let { formState, stats, nodeData } = this.props
let { tolRange, chart } = formState
tolRange = tolRange || []
let labels = beautifyLabels(tolRange)
// let labels = tolRange.map(x => Math.round(x * 1000) / 1000)
let datasets = Object.keys(stats).map(name => ({
...datasetTemplates[name],
data: stats[name]
}))
datasets.push({ ...datasetTemplates.numberOfNodes, data: nodeData })
let data = { datasets, labels }
addLegendEntry(data, `High quality mode: ${formState.highQual}`)
addLegendEntry(data, `Platform: ${platform.description}`)
return (
<div className="component chartComponent">
<h2>Chart</h2>
{chart === 'wasmVsJs' && <WasmVsJsChart data={data} />}
{chart === 'wasmStack' && <WasmStackChart data={data} />}
{chart === 'simplifyJSStack' && <JsStackChart data={data} />}
</div>
)
}
}
class WasmVsJsChart extends React.Component {
constructor(props) {
super(props)
this.state = {
scaleY: 'linear',
values: 'mean'
}
}
convertDataToHz(data) {
let datasets = data.datasets
for (let dataset of datasets.filter(d => d.id !== 'numberOfNodes')) {
dataset.data = dataset.data.map(mean => 1000 / mean)
}
}
render() {
let data = JSON.parse(JSON.stringify(this.props.data)) // deep copy
let options = genOptions(
'Simplify.wasm vs. Simplify.js',
false,
this.state.scaleY,
this.state.values
)
if (this.state.values === 'hz') this.convertDataToHz(data)
return (
<>
<Line
ref={ref => (this.ref = ref)}
height={400}
width={400}
data={data}
options={options}
/>
<label>Scale Y-Axis</label>
<select
name="scaleY"
value={this.state.scaleY}
onChange={e => this.setState({ scaleY: e.target.value })}
>
<option value="linear">Linear</option>
<option value="logarithmic">Logarithmic</option>
</select>
<label>Values: </label>
<select
name="values"
value={this.state.values}
onChange={e => this.setState({ values: e.target.value })}
>
<option value="mean">ms per operation (mean)</option>
<option value="hz">Operations per second (hz)</option>
</select>
<button onClick={() => saveChart(this.ref)}>Save chart</button>
</>
)
}
}
/**
* Returns the dataset specified by label
*/
function getDataset(data, id) {
return data.datasets.filter(d => d.id === id)[0]
}
/**
* Takes three data arrays. Subtracts the values of arr1 and arr2 from whole.
* This is used for calculating the time of execution in cases where the data
* transformation is part of the whole operation.
* Example: subtract storeCoords and loadResult from the whole simplifyWASM
* operation to get execution time of wasm code.
*/
function calculateTimeDelta(whole, arr1, arr2) {
const safeGet = (arr, idx) => (idx < arr.length ? arr[idx] : 0)
return whole.map((val, i) => val - (safeGet(arr1, i) + safeGet(arr2, i)))
}
function modifyDataArrays(data, exec, pre, after, newDatasetLabel) {
if (getDataset(data, exec) && getDataset(data, pre)) {
data.datasets.splice(1, 0, {
id: 'execution',
backgroundColor: 'red',
borderColor: 'red',
label: newDatasetLabel,
data: calculateTimeDelta(
getDataset(data, exec).data,
getDataset(data, pre).data,
getDataset(data, after).data
),
stack: 'a'
})
for (let dataset of data.datasets) {
if (dataset.id === exec) {
dataset.label = 'Total execution time'
dataset.stack = 'b'
dataset.backgroundColor = '#ff7a6b'
dataset.borderColor = '#ff7a6b'
}
}
}
}
class WasmStackChart extends React.Component {
render() {
const data = this.props.data
modifyDataArrays(
data,
'simplifyWASM',
'storeCoords',
'loadResult',
'Simplification'
)
const options = genOptions('Simplify.wasm runtime insights', true)
return (
<>
<Bar
ref={ref => (this.ref = ref)}
height={400}
width={400}
data={data}
options={options}
/>
<button onClick={() => saveChart(this.ref)}>Save chart</button>
</>
)
}
}
class JsStackChart extends React.Component {
render() {
const data = this.props.data
const options = genOptions(
'Simplify.js with coordinates transformation',
true
)
modifyDataArrays(
data,
'origSimplifyWithTransformCase',
'transformToObjectFormCase',
'transformToArrayFormCase',
'Simplification'
)
return (
<>
<Bar
ref={ref => (this.ref = ref)}
height={400}
width={400}
data={data}
options={options}
/>
<button onClick={() => saveChart(this.ref)}>Save chart</button>
</>
)
}
}
export const DebouncedChart = debounceRender(Chart, 300)
/**
* Downloads the chart config as json
*/
function saveChart(chartRef) {
let myChart = chartRef.chartInstance.config
let config = JSON.stringify(myChart, (key, val) =>
key === '_meta' ? undefined : val
)
downloadAsJson(config, 'config.json')
}
/**
* Adds an empty dataset to attach info to legend
* @param {} data
* @param {*} label
*/
function addLegendEntry(data, label) {
data.datasets.push({ label, data: [] })
}