mirror of
https://github.com/OpenFOAM/ThirdParty-6.git
synced 2025-12-08 06:57:43 +00:00
442 lines
17 KiB
JavaScript
442 lines
17 KiB
JavaScript
/**
|
|
* VTK-Web Widget Library.
|
|
*
|
|
* This module extend jQuery object to add support for graphical components
|
|
* related to 2D chart visualization. This widget depends on D3 and Rickshaw.
|
|
*
|
|
* @class jQuery.vtk.ui.Chart
|
|
*/
|
|
(function ($) {
|
|
|
|
// =======================================================================
|
|
// ==== Defaults constant values =========================================
|
|
// =======================================================================
|
|
var GRAPH_HTML_TEMPLATE = [
|
|
"<div class='vtk-legend'></div>",
|
|
"<div class='vtk-top' style='left: AXIS_SIZE px; top: 0px; height: AXIS_SIZE px; position: absolute;'></div>",
|
|
"<div class='vtk-left' style='left: 0 px; top: AXIS_SIZE px; width: AXIS_SIZE px; position: absolute;'></div>",
|
|
"<div class='vtk-center' style='left: AXIS_SIZE px; top: AXIS_SIZE px; position: relative;'></div>",
|
|
"<div class='vtk-right' style='right: 0px; top: AXIS_SIZE px; position: absolute;'></div>",
|
|
"<div class='vtk-bottom' style='left: AXIS_SIZE px; bottom: 0px; position: absolute;'></div>",
|
|
"<div class='vtk-annotation' style='left: AXIS_SIZE px; bottom: 0px; position: absolute;'></div>"
|
|
];
|
|
|
|
// =======================================================================
|
|
|
|
function toNumber(str) {
|
|
return Number(str.replace(/^\s+|\s+$/g, ''));
|
|
}
|
|
|
|
// =======================================================================
|
|
|
|
function extractColumnHeaderMap(headerLine) {
|
|
var header = headerLine.split(','),
|
|
colIdxMap = {};
|
|
for(var idx in header) {
|
|
colIdxMap[header[idx]] = idx;
|
|
}
|
|
return colIdxMap;
|
|
}
|
|
|
|
// =======================================================================
|
|
|
|
function singleDataCSVConverter(inputString, outputSeries, options) {
|
|
var lines = inputString.split('\n'),
|
|
data = [],
|
|
serie = $.extend({data:data}, options),
|
|
nbLines = lines.length;
|
|
|
|
// Process data
|
|
for(var i = 1; i < nbLines; ++i) {
|
|
var values = lines[i].split(',');
|
|
if(values.length === 2) {
|
|
item = { x: toNumber(values[0]), y: toNumber(values[1]) };
|
|
if(isNaN(item.y)) {
|
|
item.y = null;
|
|
}
|
|
data.push(item);
|
|
}
|
|
}
|
|
outputSeries.push(serie);
|
|
}
|
|
|
|
// =======================================================================
|
|
|
|
function multiDataCSVConverter(inputString, outputSeries, options) {
|
|
var lines = inputString.split('\n'),
|
|
header = lines[0].split(','),
|
|
headerMap = extractColumnHeaderMap(lines[0]),
|
|
nbLines = lines.length,
|
|
nbValuesByLines = header.length,
|
|
xHeaderName = options['x'],
|
|
xIdx = headerMap[xHeaderName],
|
|
series = [],
|
|
palette = new Rickshaw.Color.Palette();
|
|
|
|
if(options.hasOwnProperty('palette') && options['palette'] !== null) {
|
|
palette = options['palette'];
|
|
}
|
|
|
|
// Remove time field
|
|
header.splice(header.indexOf(xHeaderName), 1);
|
|
|
|
// Create series
|
|
for(var idx in header) {
|
|
var serie = {
|
|
data: [],
|
|
color: palette.color(),
|
|
name: header[idx]
|
|
};
|
|
|
|
series.push(serie);
|
|
outputSeries.push(serie);
|
|
}
|
|
|
|
// Process data
|
|
for(var i = 1; i < nbLines; ++i) {
|
|
var values = lines[i].split(',');
|
|
if(values.length === nbValuesByLines) {
|
|
xValue = toNumber(values[xIdx]);
|
|
for(var idx in header) {
|
|
var item = { x: xValue, y: toNumber(values[headerMap[header[idx]]])};
|
|
if(isNaN(item.y)) {
|
|
item.y = null;
|
|
}
|
|
if(!isNaN(item.x)) {
|
|
series[idx].data.push(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// =======================================================================
|
|
|
|
function updateLegend(container) {
|
|
var legendContainer = $('.vtk-legend', container),
|
|
chart = container.data('chart'),
|
|
legend = chart['legends'].basic;
|
|
|
|
// Empty UI
|
|
legendContainer.children("ul").empty();
|
|
|
|
// Update model
|
|
if(legend !== undefined) {
|
|
legend.lines = [];
|
|
var series = chart.graph.series.map( function(s) { return s } )
|
|
series.forEach(function(s) {
|
|
legend.addLine(s);
|
|
});
|
|
}
|
|
}
|
|
|
|
// =======================================================================
|
|
|
|
/**
|
|
* Method used to create a 2D chart based on some available data.
|
|
*
|
|
* @member jQuery.vtk.ui.Chart
|
|
* @method vtkChart
|
|
* @param {Object} configuration
|
|
*
|
|
* Usage:
|
|
* var options = {
|
|
* 'renderer': 'line', // Type of chart [line, area, bar, scatterplot]
|
|
* 'stacked' : false,
|
|
* 'series': [
|
|
* {
|
|
* data: [ { x:0, y:0 }, { x:100, y:10 }, { x:200, y:5 }, { x:300, y:20 }, { x:400, y:25 }, { x:1000, y:-10 } ],
|
|
* color: 'steelblue',
|
|
* name: 'field 0'
|
|
* },{
|
|
* data: [ { x:0, y:20 }, { x:100, y:30 }, { x:200, y:25 }, { x:300, y:40 }, { x:400, y:55 }, { x:1000, y:-10 } ],
|
|
* color: 'lightblue',
|
|
* name: 'field 1'
|
|
* }
|
|
* ],
|
|
* 'axes': [ "bottom", "left", "top"], // Draw axis on border with scale
|
|
* 'chart-padding': [0, 150, 50, 0], // Graph padding [top, right, bottom, left] in px. Useful to save space for legend
|
|
* };
|
|
*
|
|
* $('.chart-container-div').vtkChart(options);
|
|
*/
|
|
|
|
$.fn.vtkChart = function(options) {
|
|
// Handle data with default values
|
|
var opts = $.extend({},$.fn.vtkChart.defaults, options);
|
|
|
|
return this.each(function() {
|
|
var me = $(this).empty().addClass('vtk-chart'),
|
|
container = $("<div/>", {
|
|
html: GRAPH_HTML_TEMPLATE.join('').replace(/AXIS_SIZE /g, opts.axisThickness)
|
|
}),
|
|
chartContainer = $('.vtk-center', container),
|
|
legendContainer = $('.vtk-legend', container),
|
|
axisContainer = {
|
|
bottom: $('.vtk-bottom', container)[0],
|
|
top: $('.vtk-top', container)[0],
|
|
left: $('.vtk-left', container)[0],
|
|
right: $('.vtk-right', container)[0]
|
|
},
|
|
annotationContainer = $('.vtk-annotation', container);
|
|
me.append(container);
|
|
// container.css('width', (opts['width']+(2*opts.axisThickness)) + 'px');
|
|
|
|
var graphOptions = {
|
|
element: chartContainer[0],
|
|
width: opts['width'],
|
|
height: opts['height'],
|
|
renderer: opts['renderer'],
|
|
min: 'auto',
|
|
stroke: true,
|
|
series: opts['series']
|
|
},
|
|
graph = new Rickshaw.Graph(graphOptions),
|
|
axes = [],
|
|
legends = {},
|
|
annotator = null,
|
|
data = {
|
|
configuration: graphOptions,
|
|
options: opts,
|
|
palette: new Rickshaw.Color.Palette(),
|
|
graph: graph,
|
|
axes: axes,
|
|
legends: legends
|
|
};
|
|
|
|
graph.renderer.unstack = !opts.stacked;
|
|
graph.render();
|
|
|
|
// Complete graph accessories
|
|
// => Axis
|
|
for(var idx in opts.axes) {
|
|
var orientation = opts.axes[idx], axis = null;
|
|
if(orientation === 'top' || orientation === 'bottom') {
|
|
axis = new Rickshaw.Graph.Axis.X({graph: graph, orientation: orientation, element: axisContainer[orientation]});
|
|
} else {
|
|
axis = new Rickshaw.Graph.Axis.Y({graph: graph, orientation: orientation, element: axisContainer[orientation]});
|
|
}
|
|
axes.push(axis);
|
|
}
|
|
// => Legend
|
|
if(opts.legend.basic) {
|
|
legends['basic'] = new Rickshaw.Graph.Legend({graph: graph, element: legendContainer[0]});
|
|
// if(opts.legend.toggle) {
|
|
// legends['toggle'] = new Rickshaw.Graph.Behavior.Series.Toggle({graph: graph, legend: legends['basic']});
|
|
// }
|
|
// if(opts.legend.highlight) {
|
|
// legends['highlight'] = new Rickshaw.Graph.Behavior.Series.Highlight({graph: graph, legend: legends['basic']});
|
|
// }
|
|
}
|
|
// => Hover
|
|
if(opts.hover !== null) {
|
|
data['hover'] = new Rickshaw.Graph.HoverDetail({
|
|
graph: graph,
|
|
xFormatter: opts.hover.xFormatter,
|
|
yFormatter: opts.hover.yFormatter
|
|
});
|
|
}
|
|
// => Annotation
|
|
data['annotator'] = new Rickshaw.Graph.Annotate({
|
|
graph: graph,
|
|
element: annotationContainer[0]
|
|
});
|
|
for(var idx in opts.annotations) {
|
|
var annotation = opts.annotations[idx];
|
|
data['annotator'].add(annotation['time'], annotation['message']);
|
|
}
|
|
|
|
// Handle auto-resize
|
|
if(opts.autosize) {
|
|
function autoResize() {
|
|
var w = $(window),
|
|
padding = opts['chart-padding'],
|
|
thickness = opts.axisThickness,
|
|
size = { width: me.width() - (2*thickness) - (padding[1] + padding[3]), height: me.height() - (2*thickness) - (padding[0] + padding[2])};
|
|
$('.vtk-bottom, .vtk-top, .vtk-annotation', me).css('height', thickness +'px').css('width', size['width'] +'px');
|
|
$('.vtk-left, .vtk-right', me).css('width', thickness +'px').css('height', size['height'] +'px');
|
|
$('.vtk-right', me).css('right', padding[1] + 'px').css('top', (padding[0] + thickness) + 'px');
|
|
$('.vtk-left', me).css('left', padding[3] + 'px').css('top', (padding[0] + thickness) + 'px');
|
|
$('.vtk-top', me).css('top', padding[0] + 'px').css('left', (thickness+padding[3]) + 'px');
|
|
$('.vtk-bottom, .vtk-annotation', me).css('left', (thickness+padding[3]) + 'px');
|
|
$('.vtk-bottom', me).css('bottom', padding[2] + 'px');
|
|
$('.vtk-center', me).css('width', (size['width'] - 2*thickness - (padding[1] + padding[3]))+'px').css('height', (size['height'] - 2*thickness - (padding[0] + padding[2]))+'px').css('left', (padding[3]+thickness) + 'px').css('top', (padding[0]+thickness) + 'px');
|
|
|
|
data.graph.configure(size);
|
|
data.graph.update();
|
|
}
|
|
|
|
$(window).resize(autoResize).trigger('resize');
|
|
}
|
|
|
|
// Save data
|
|
me.data('chart', data);
|
|
graph.render();
|
|
});
|
|
};
|
|
|
|
// =======================================================================
|
|
/**
|
|
* Method used to update the data of the 2D chart.
|
|
*
|
|
* @member jQuery.vtk.ui.Chart
|
|
* @method vtkChartUpdateData
|
|
* @param {Array} series
|
|
* @param {boolean} replace previous series
|
|
*
|
|
* Usage:
|
|
* var series: [
|
|
* {
|
|
* data: [ { x:0, y:0 }, { x:100, y:10 }, { x:200, y:5 }, { x:300, y:20 }, { x:400, y:25 }, { x:1000, y:-10 } ],
|
|
* color: 'steelblue',
|
|
* name: 'field 0'
|
|
* },{
|
|
* data: [ { x:0, y:20 }, { x:100, y:30 }, { x:200, y:25 }, { x:300, y:40 }, { x:400, y:55 }, { x:1000, y:-10 } ],
|
|
* color: 'lightblue',
|
|
* name: 'field 1'
|
|
* }
|
|
* ];
|
|
*
|
|
* $('.chart-container-div').vtkChartUpdateData(series);
|
|
*/
|
|
$.fn.vtkChartUpdateData = function(series, replace) {
|
|
return this.each(function() {
|
|
var me = $(this),
|
|
data = me.data('chart'),
|
|
dataset = data['configuration']['series'];
|
|
if(replace) {
|
|
while(dataset.length > 0) {
|
|
dataset.pop();
|
|
}
|
|
}
|
|
for(var idx in series) {
|
|
data.graph.series.push(series[idx]);
|
|
}
|
|
data.graph.validateSeries(data.graph.series);
|
|
data.graph.update();
|
|
updateLegend(me);
|
|
});
|
|
}
|
|
|
|
// =======================================================================
|
|
|
|
/**
|
|
* Method used to update the data of the 2D chart.
|
|
*
|
|
* @member jQuery.vtk.ui.Chart
|
|
* @method vtkChartFetchData
|
|
* @param {Object} options
|
|
*
|
|
* Usage:
|
|
* var options_json = { replace: true, url: "data.json", type: 'json', converter: null };
|
|
* var options_csv_1 = { replace: true, url: "data1.csv", type: 'csv-xy', options: { name: 'Temperature', color: palette.color(), ... } };
|
|
* var options_csv_n = { replace: true, url: "data2.csv", type: 'csv-x*', options: { x: 'time', palette: null } };
|
|
*
|
|
* $('.chart-container-div').vtkChartFetchData(options_*);
|
|
*
|
|
* Where data looks like:
|
|
*
|
|
* data.json
|
|
* [
|
|
* {
|
|
* data: [ { x:0, y:0 }, { x:100, y:10 }, { x:200, y:5 }, { x:300, y:20 }, { x:400, y:25 }, { x:1000, y:-10 } ],
|
|
* color: 'steelblue',
|
|
* name: 'field 0'
|
|
* },{
|
|
* data: [ { x:0, y:20 }, { x:100, y:30 }, { x:200, y:25 }, { x:300, y:40 }, { x:400, y:55 }, { x:1000, y:-10 } ],
|
|
* color: 'lightblue',
|
|
* name: 'field 1'
|
|
* }
|
|
* ]
|
|
*
|
|
*
|
|
* data1.csv
|
|
* x,y
|
|
* 0,0
|
|
* 1,0.234
|
|
* 2,0.5
|
|
* 2.5,7
|
|
*
|
|
*
|
|
* data2.csv
|
|
* time,x,y,z
|
|
* 0,0,0,0
|
|
* 1,0.234,1.2,7.6
|
|
* 2,0.5,3,6
|
|
* 2.5,7,8,9
|
|
*/
|
|
$.fn.vtkChartFetchData = function(info) {
|
|
return this.each(function() {
|
|
var me = $(this),
|
|
data = me.data('chart'),
|
|
options = info['options'];
|
|
|
|
$.ajax({
|
|
url: info.url,
|
|
dataType: "text"
|
|
}).done(function(data){
|
|
var series = [];
|
|
if (info.type === 'json') {
|
|
series = $.parseJSON(data);
|
|
} else if(info.type === 'csv-xy') {
|
|
singleDataCSVConverter(data, series, options);
|
|
} else if(info.type === 'csv-x*') {
|
|
multiDataCSVConverter(data, series, options);
|
|
}
|
|
me.vtkChartUpdateData(series, info['replace']);
|
|
});
|
|
});
|
|
}
|
|
|
|
// =======================================================================
|
|
/**
|
|
* Method used to update the data of the 2D chart.
|
|
*
|
|
* @member jQuery.vtk.ui.Chart
|
|
* @method vtkChartConfigure
|
|
* @param {Object} options
|
|
*
|
|
* Usage:
|
|
*
|
|
* var options = {
|
|
* 'renderer': 'line', // Type of chart [line, area, bar, scatterplot]
|
|
* 'stacked' : false,
|
|
* 'axes': [ "bottom", "left", "top"], // Draw axis on border with scale
|
|
* 'chart-padding': [0, 150, 50, 0], // Graph padding [top, right, bottom, left] in px. Useful to save space for legend
|
|
* };
|
|
* $('.chart-container-div').vtkChartConfigure(options);
|
|
*/
|
|
|
|
$.fn.vtkChartConfigure = function(conf) {
|
|
return this.each(function() {
|
|
var me = $(this),
|
|
data = me.data('chart');
|
|
var opts = $.extend(data['options']['configuration'], conf);
|
|
$('.x_axis_d3', me).height(data.axisThickness + 'px').width(($(window).width()-(2*data.axisThickness)) + 'px');
|
|
data.graph.configure(opts);
|
|
data.graph.update();
|
|
});
|
|
};
|
|
|
|
// =======================================================================
|
|
|
|
$.fn.vtkChart.defaults = {
|
|
width: 300,
|
|
height: 200,
|
|
axisThickness: 25,
|
|
autosize: true,
|
|
stacked: false,
|
|
renderer: "line",
|
|
interpolation: "linear",
|
|
series: [],
|
|
hover: { xFormatter: function(x) { return x; }, yFormatter: function(y) {return y;} },
|
|
legend: { basic: true, toggle: true, highlight: true },
|
|
annotations: [], // { time: 0, message: "Just a text" } ...
|
|
axes: [ "bottom", "left" ],
|
|
'chart-padding': [0, 0, 0, 0]
|
|
};
|
|
|
|
// =======================================================================
|
|
|
|
}(jQuery));
|