@ -6,8 +6,9 @@ var Component = require('./Component');
var DataAxis = require ( './DataAxis' ) ;
var DataAxis = require ( './DataAxis' ) ;
var GraphGroup = require ( './GraphGroup' ) ;
var GraphGroup = require ( './GraphGroup' ) ;
var Legend = require ( './Legend' ) ;
var Legend = require ( './Legend' ) ;
var BarFunctions = require ( './graph2d_types/bar' ) ;
var LineFunctions = require ( './graph2d_types/line' ) ;
var Bars = require ( './graph2d_types/bar' ) ;
var Lines = require ( './graph2d_types/line' ) ;
var Points = require ( './graph2d_types/points' ) ;
var UNGROUPED = '__ungrouped__' ; // reserved group id for ungrouped items
var UNGROUPED = '__ungrouped__' ; // reserved group id for ungrouped items
@ -27,11 +28,11 @@ function LineGraph(body, options) {
defaultGroup : 'default' ,
defaultGroup : 'default' ,
sort : true ,
sort : true ,
sampling : true ,
sampling : true ,
stack : false ,
stack : false ,
graphHeight : '400px' ,
graphHeight : '400px' ,
shaded : {
shaded : {
enabled : false ,
enabled : false ,
orientation : 'bottom' // top, bottom
orientation : 'bottom' // top, bottom, zero
} ,
} ,
style : 'line' , // line, bar
style : 'line' , // line, bar
barChart : {
barChart : {
@ -56,15 +57,19 @@ function LineGraph(body, options) {
width : '40px' ,
width : '40px' ,
visible : true ,
visible : true ,
alignZeros : true ,
alignZeros : true ,
left : {
range : { min : undefined , max : undefined } ,
format : function ( value ) { return value ; } ,
title : { text : undefined , style : undefined }
left : {
range : { min : undefined , max : undefined } ,
format : function ( value ) {
return value ;
} ,
title : { text : undefined , style : undefined }
} ,
} ,
right : {
range : { min : undefined , max : undefined } ,
format : function ( value ) { return value ; } ,
title : { text : undefined , style : undefined }
right : {
range : { min : undefined , max : undefined } ,
format : function ( value ) {
return value ;
} ,
title : { text : undefined , style : undefined }
}
}
} ,
} ,
legend : {
legend : {
@ -133,10 +138,10 @@ function LineGraph(body, options) {
this . setOptions ( options ) ;
this . setOptions ( options ) ;
this . groupsUsingDefaultStyles = [ 0 ] ;
this . groupsUsingDefaultStyles = [ 0 ] ;
this . COUNTER = 0 ;
this . COUNTER = 0 ;
this . body . emitter . on ( 'rangechanged' , function ( ) {
this . body . emitter . on ( 'rangechanged' , function ( ) {
me . lastStart = me . body . range . start ;
me . lastStart = me . body . range . start ;
me . svg . style . left = util . option . asSize ( - me . props . width ) ;
me . svg . style . left = util . option . asSize ( - me . props . width ) ;
me . redraw . call ( me , true ) ;
me . redraw . call ( me , true ) ;
} ) ;
} ) ;
// create the HTML DOM
// create the HTML DOM
@ -151,15 +156,15 @@ LineGraph.prototype = new Component();
/ * *
/ * *
* Create the HTML DOM for the ItemSet
* Create the HTML DOM for the ItemSet
* /
* /
LineGraph . prototype . _create = function ( ) {
LineGraph . prototype . _create = function ( ) {
var frame = document . createElement ( 'div' ) ;
var frame = document . createElement ( 'div' ) ;
frame . className = 'vis-line-graph' ;
frame . className = 'vis-line-graph' ;
this . dom . frame = frame ;
this . dom . frame = frame ;
// create svg element for graph drawing.
// create svg element for graph drawing.
this . svg = document . createElementNS ( 'http://www.w3.org/2000/svg' , 'svg' ) ;
this . svg = document . createElementNS ( 'http://www.w3.org/2000/svg' , 'svg' ) ;
this . svg . style . position = 'relative' ;
this . svg . style . position = 'relative' ;
this . svg . style . height = ( '' + this . options . graphHeight ) . replace ( 'px' , '' ) + 'px' ;
this . svg . style . height = ( '' + this . options . graphHeight ) . replace ( 'px' , '' ) + 'px' ;
this . svg . style . display = 'block' ;
this . svg . style . display = 'block' ;
frame . appendChild ( this . svg ) ;
frame . appendChild ( this . svg ) ;
@ -182,23 +187,23 @@ LineGraph.prototype._create = function(){
* set the options of the LineGraph . the mergeOptions is used for subObjects that have an enabled element .
* set the options of the LineGraph . the mergeOptions is used for subObjects that have an enabled element .
* @ param { object } options
* @ param { object } options
* /
* /
LineGraph . prototype . setOptions = function ( options ) {
LineGraph . prototype . setOptions = function ( options ) {
if ( options ) {
if ( options ) {
var fields = [ 'sampling' , 'defaultGroup' , 'stack' , 'height' , 'graphHeight' , 'yAxisOrientation' , 'style' , 'barChart' , 'dataAxis' , 'sort' , 'groups' ] ;
var fields = [ 'sampling' , 'defaultGroup' , 'stack' , 'height' , 'graphHeight' , 'yAxisOrientation' , 'style' , 'barChart' , 'dataAxis' , 'sort' , 'groups' ] ;
if ( options . graphHeight === undefined && options . height !== undefined && this . body . domProps . centerContainer . height !== undefined ) {
if ( options . graphHeight === undefined && options . height !== undefined && this . body . domProps . centerContainer . height !== undefined ) {
this . updateSVGheight = true ;
this . updateSVGheight = true ;
this . updateSVGheightOnResize = true ;
this . updateSVGheightOnResize = true ;
}
}
else if ( this . body . domProps . centerContainer . height !== undefined && options . graphHeight !== undefined ) {
else if ( this . body . domProps . centerContainer . height !== undefined && options . graphHeight !== undefined ) {
if ( parseInt ( ( options . graphHeight + '' ) . replace ( "px" , '' ) ) < this . body . domProps . centerContainer . height ) {
if ( parseInt ( ( options . graphHeight + '' ) . replace ( "px" , '' ) ) < this . body . domProps . centerContainer . height ) {
this . updateSVGheight = true ;
this . updateSVGheight = true ;
}
}
}
}
util . selectiveDeepExtend ( fields , this . options , options ) ;
util . selectiveDeepExtend ( fields , this . options , options ) ;
util . mergeOptions ( this . options , options , 'interpolation' ) ;
util . mergeOptions ( this . options , options , 'drawPoints' ) ;
util . mergeOptions ( this . options , options , 'shaded' ) ;
util . mergeOptions ( this . options , options , 'legend' ) ;
util . mergeOptions ( this . options , options , 'interpolation' ) ;
util . mergeOptions ( this . options , options , 'drawPoints' ) ;
util . mergeOptions ( this . options , options , 'shaded' ) ;
util . mergeOptions ( this . options , options , 'legend' ) ;
if ( options . interpolation ) {
if ( options . interpolation ) {
if ( typeof options . interpolation == 'object' ) {
if ( typeof options . interpolation == 'object' ) {
@ -245,7 +250,7 @@ LineGraph.prototype.setOptions = function(options) {
/ * *
/ * *
* Hide the component from the DOM
* Hide the component from the DOM
* /
* /
LineGraph . prototype . hide = function ( ) {
LineGraph . prototype . hide = function ( ) {
// remove the frame containing the items
// remove the frame containing the items
if ( this . dom . frame . parentNode ) {
if ( this . dom . frame . parentNode ) {
this . dom . frame . parentNode . removeChild ( this . dom . frame ) ;
this . dom . frame . parentNode . removeChild ( this . dom . frame ) ;
@ -257,7 +262,7 @@ LineGraph.prototype.hide = function() {
* Show the component in the DOM ( when not already visible ) .
* Show the component in the DOM ( when not already visible ) .
* @ return { Boolean } changed
* @ return { Boolean } changed
* /
* /
LineGraph . prototype . show = function ( ) {
LineGraph . prototype . show = function ( ) {
// show frame containing the items
// show frame containing the items
if ( ! this . dom . frame . parentNode ) {
if ( ! this . dom . frame . parentNode ) {
this . body . dom . center . appendChild ( this . dom . frame ) ;
this . body . dom . center . appendChild ( this . dom . frame ) ;
@ -269,7 +274,7 @@ LineGraph.prototype.show = function() {
* Set items
* Set items
* @ param { vis . DataSet | null } items
* @ param { vis . DataSet | null } items
* /
* /
LineGraph . prototype . setItems = function ( items ) {
LineGraph . prototype . setItems = function ( items ) {
var me = this ,
var me = this ,
ids ,
ids ,
oldItemsData = this . itemsData ;
oldItemsData = this . itemsData ;
@ -315,7 +320,7 @@ LineGraph.prototype.setItems = function(items) {
* Set groups
* Set groups
* @ param { vis . DataSet } groups
* @ param { vis . DataSet } groups
* /
* /
LineGraph . prototype . setGroups = function ( groups ) {
LineGraph . prototype . setGroups = function ( groups ) {
var me = this ;
var me = this ;
var ids ;
var ids ;
@ -362,20 +367,23 @@ LineGraph.prototype.setGroups = function(groups) {
* @ param [ ids ]
* @ param [ ids ]
* @ private
* @ private
* /
* /
LineGraph . prototype . _onUpdate = function ( ids ) {
LineGraph . prototype . _onUpdate = function ( ids ) {
this . _updateAllGroupData ( ) ;
this . _updateAllGroupData ( ) ;
this . redraw ( true ) ;
this . redraw ( true ) ;
} ;
} ;
LineGraph . prototype . _onAdd = function ( ids ) { this . _onUpdate ( ids ) ; } ;
LineGraph . prototype . _onRemove = function ( ids ) { this . _onUpdate ( ids ) ; } ;
LineGraph . prototype . _onUpdateGroups = function ( groupIds ) {
for ( var i = 0 ; i < groupIds . length ; i ++ ) {
var group = this . groupsData . get ( groupIds [ i ] ) ;
this . _updateGroup ( group , groupIds [ i ] ) ;
}
LineGraph . prototype . _onAdd = function ( ids ) {
this . _onUpdate ( ids ) ;
} ;
LineGraph . prototype . _onRemove = function ( ids ) {
this . _onUpdate ( ids ) ;
} ;
LineGraph . prototype . _onUpdateGroups = function ( groupIds ) {
this . _updateAllGroupData ( ) ;
this . redraw ( true ) ;
this . redraw ( true ) ;
} ;
} ;
LineGraph . prototype . _onAddGroups = function ( groupIds ) { this . _onUpdateGroups ( groupIds ) ; } ;
LineGraph . prototype . _onAddGroups = function ( groupIds ) {
this . _onUpdateGroups ( groupIds ) ;
} ;
/ * *
/ * *
@ -446,19 +454,38 @@ LineGraph.prototype._updateGroup = function (group, groupId) {
LineGraph . prototype . _updateAllGroupData = function ( ) {
LineGraph . prototype . _updateAllGroupData = function ( ) {
if ( this . itemsData != null ) {
if ( this . itemsData != null ) {
var groupsContent = { } ;
var groupsContent = { } ;
this . itemsData . get ( ) . forEach ( function ( item ) {
var items = this . itemsData . get ( ) ;
//pre-Determine array sizes, for more efficient memory claim
var groupCounts = { } ;
for ( var i = 0 ; i < items . length ; i ++ ) {
var item = items [ i ] ;
var groupId = item . group ;
if ( groupId === null || groupId === undefined ) {
groupId = UNGROUPED ;
}
groupCounts . hasOwnProperty ( groupId ) ? groupCounts [ groupId ] ++ : groupCounts [ groupId ] = 1 ;
}
//Now insert data into the arrays.
for ( var i = 0 ; i < items . length ; i ++ ) {
var item = items [ i ] ;
var groupId = item . group ;
var groupId = item . group ;
if ( groupId === null || groupId === undefined ) {
if ( groupId === null || groupId === undefined ) {
groupId = UNGROUPED ;
groupId = UNGROUPED ;
}
}
if ( groupsContent [ groupId ] === undefined ) {
groupsContent [ groupId ] = [ ] ;
if ( ! groupsContent . hasOwnProperty ( groupId ) ) {
groupsContent [ groupId ] = new Array ( groupCounts [ groupId ] ) ;
}
}
var extended = Object . create ( item ) ;
//Copy data (because of unmodifiable DataView input.
var extended = util . bridgeObject ( item ) ;
extended . x = util . convert ( item . x , 'Date' ) ;
extended . x = util . convert ( item . x , 'Date' ) ;
groupsContent [ groupId ] . push ( extended ) ;
} ) ;
//Update legendas and axis
extended . orginalY = item . y ; //real Y
// typecast all items to numbers. Takes around 10ms for 500.000 items
extended . y = Number ( item . y ) ;
var index = groupsContent [ groupId ] . length - groupCounts [ groupId ] -- ;
groupsContent [ groupId ] [ index ] = extended ;
}
//Update legendas, style and axis
for ( var groupId in groupsContent ) {
for ( var groupId in groupsContent ) {
if ( groupsContent . hasOwnProperty ( groupId ) ) {
if ( groupsContent . hasOwnProperty ( groupId ) ) {
if ( groupsContent [ groupId ] . length == 0 ) {
if ( groupsContent [ groupId ] . length == 0 ) {
@ -466,13 +493,14 @@ LineGraph.prototype._updateAllGroupData = function () {
this . _onRemoveGroups ( [ groupId ] ) ;
this . _onRemoveGroups ( [ groupId ] ) ;
}
}
} else {
} else {
if ( ! this . groups . hasOwnProperty ( groupId ) ) {
var group = { id : groupId , content : this . options . defaultGroup } ;
if ( this . groupsData && this . groupsData . hasOwnProperty ( groupId ) ) {
group = this . groupsData [ groupId ] ;
}
this . _updateGroup ( group , groupId ) ;
var group = undefined ;
if ( this . groupsData != undefined ) {
group = this . groupsData . get ( groupId ) ;
}
if ( group == undefined ) {
group = { id : groupId , content : this . options . defaultGroup + groupId } ;
}
}
this . _updateGroup ( group , groupId ) ;
this . groups [ groupId ] . setItems ( groupsContent [ groupId ] ) ;
this . groups [ groupId ] . setItems ( groupsContent [ groupId ] ) ;
}
}
}
}
@ -484,14 +512,14 @@ LineGraph.prototype._updateAllGroupData = function () {
* Redraw the component , mandatory function
* Redraw the component , mandatory function
* @ return { boolean } Returns true if the component is resized
* @ return { boolean } Returns true if the component is resized
* /
* /
LineGraph . prototype . redraw = function ( forceGraphUpdate ) {
LineGraph . prototype . redraw = function ( forceGraphUpdate ) {
var resized = false ;
var resized = false ;
// calculate actual size and position
// calculate actual size and position
this . props . width = this . dom . frame . offsetWidth ;
this . props . width = this . dom . frame . offsetWidth ;
this . props . height = this . body . domProps . centerContainer . height
this . props . height = this . body . domProps . centerContainer . height
- this . body . domProps . border . top
- this . body . domProps . border . bottom ;
- this . body . domProps . border . top
- this . body . domProps . border . bottom ;
// update the graph if there is no lastWidth or with, used for the initial draw
// update the graph if there is no lastWidth or with, used for the initial draw
if ( this . lastWidth === undefined && this . props . width ) {
if ( this . lastWidth === undefined && this . props . width ) {
@ -510,7 +538,7 @@ LineGraph.prototype.redraw = function(forceGraphUpdate) {
// the svg element is three times as big as the width, this allows for fully dragging left and right
// the svg element is three times as big as the width, this allows for fully dragging left and right
// without reloading the graph. the controls for this are bound to events in the constructor
// without reloading the graph. the controls for this are bound to events in the constructor
if ( resized == true ) {
if ( resized == true ) {
this . svg . style . width = util . option . asSize ( 3 * this . props . width ) ;
this . svg . style . width = util . option . asSize ( 3 * this . props . width ) ;
this . svg . style . left = util . option . asSize ( - this . props . width ) ;
this . svg . style . left = util . option . asSize ( - this . props . width ) ;
// if the height of the graph is set as proportional, change the height of the svg
// if the height of the graph is set as proportional, change the height of the svg
@ -528,7 +556,7 @@ LineGraph.prototype.redraw = function(forceGraphUpdate) {
this . updateSVGheight = false ;
this . updateSVGheight = false ;
}
}
else {
else {
this . svg . style . height = ( '' + this . options . graphHeight ) . replace ( 'px' , '' ) + 'px' ;
this . svg . style . height = ( '' + this . options . graphHeight ) . replace ( 'px' , '' ) + 'px' ;
}
}
// zoomed is here to ensure that animations are shown correctly.
// zoomed is here to ensure that animations are shown correctly.
@ -541,7 +569,7 @@ LineGraph.prototype.redraw = function(forceGraphUpdate) {
var offset = this . body . range . start - this . lastStart ;
var offset = this . body . range . start - this . lastStart ;
var range = this . body . range . end - this . body . range . start ;
var range = this . body . range . end - this . body . range . start ;
if ( this . props . width != 0 ) {
if ( this . props . width != 0 ) {
var rangePerPixelInv = this . props . width / range ;
var rangePerPixelInv = this . props . width / range ;
var xOffset = offset * rangePerPixelInv ;
var xOffset = offset * rangePerPixelInv ;
this . svg . style . left = ( - this . props . width - xOffset ) + 'px' ;
this . svg . style . left = ( - this . props . width - xOffset ) + 'px' ;
}
}
@ -563,10 +591,11 @@ LineGraph.prototype._updateGraph = function () {
DOMutil . prepareElements ( this . svgElements ) ;
DOMutil . prepareElements ( this . svgElements ) ;
if ( this . props . width != 0 && this . itemsData != null ) {
if ( this . props . width != 0 && this . itemsData != null ) {
var group , i ;
var group , i ;
var preprocessedGroupData = { } ;
var processedGroupData = { } ;
var groupRanges = { } ;
var groupRanges = { } ;
var changeCalled = false ;
var changeCalled = false ;
// this is the range of the SVG canvas
var minDate = this . body . util . toGlobalTime ( - this . body . domProps . root . width ) ;
var maxDate = this . body . util . toGlobalTime ( 2 * this . body . domProps . root . width ) ;
// getting group Ids
// getting group Ids
var groupIds = [ ] ;
var groupIds = [ ] ;
@ -579,10 +608,8 @@ LineGraph.prototype._updateGraph = function () {
}
}
}
}
if ( groupIds . length > 0 ) {
if ( groupIds . length > 0 ) {
// this is the range of the SVG canvas
var minDate = this . body . util . toGlobalTime ( - this . body . domProps . root . width ) ;
var maxDate = this . body . util . toGlobalTime ( 2 * this . body . domProps . root . width ) ;
var groupsData = { } ;
var groupsData = { } ;
// fill groups data, this only loads the data we require based on the timewindow
// fill groups data, this only loads the data we require based on the timewindow
this . _getRelevantData ( groupIds , groupsData , minDate , maxDate ) ;
this . _getRelevantData ( groupIds , groupsData , minDate , maxDate ) ;
@ -591,11 +618,11 @@ LineGraph.prototype._updateGraph = function () {
// we transform the X coordinates to detect collisions
// we transform the X coordinates to detect collisions
for ( i = 0 ; i < groupIds . length ; i ++ ) {
for ( i = 0 ; i < groupIds . length ; i ++ ) {
preprocessedGroupData [ groupIds [ i ] ] = this . _convertXcoordinates ( groupsData [ groupIds [ i ] ] ) ;
this . _convertXcoordinates ( groupsData [ groupIds [ i ] ] ) ;
}
}
// now all needed data has been collected we start the processing.
// now all needed data has been collected we start the processing.
this . _getYRanges ( groupIds , preprocessedGroup Data, groupRanges ) ;
this . _getYRanges ( groupIds , groups Data, groupRanges ) ;
// update the Y axis first, we use this data to draw at the correct Y points
// update the Y axis first, we use this data to draw at the correct Y points
// changeCalled is required to clean the SVG on a change emit.
// changeCalled is required to clean the SVG on a change emit.
@ -618,17 +645,64 @@ LineGraph.prototype._updateGraph = function () {
// With the yAxis scaled correctly, use this to get the Y values of the points.
// With the yAxis scaled correctly, use this to get the Y values of the points.
for ( i = 0 ; i < groupIds . length ; i ++ ) {
for ( i = 0 ; i < groupIds . length ; i ++ ) {
group = this . groups [ groupIds [ i ] ] ;
group = this . groups [ groupIds [ i ] ] ;
processedGroupData [ groupIds [ i ] ] = this . _convertYcoordinates ( groupsData [ groupIds [ i ] ] , group ) ;
if ( this . options . stack === true && this . options . style === 'line' && i > 0 ) {
this . _stack ( groupsData [ groupIds [ i ] ] , groupsData [ groupIds [ i - 1 ] ] ) ;
}
this . _convertYcoordinates ( groupsData [ groupIds [ i ] ] , group ) ;
}
}
// draw the groups
BarFunctions . draw ( groupIds , processedGroupData , this . framework ) ;
//Precalculate paths and draw shading if appropriate. This will make sure the shading is always behind any lines.
var paths = { } ;
for ( i = 0 ; i < groupIds . length ; i ++ ) {
for ( i = 0 ; i < groupIds . length ; i ++ ) {
group = this . groups [ groupIds [ i ] ] ;
group = this . groups [ groupIds [ i ] ] ;
if ( group . options . style != 'bar' ) { // bar needs to be drawn enmasse
group . draw ( processedGroupData [ groupIds [ i ] ] , group , this . framework ) ;
if ( group . options . style === 'line' && group . options . shaded . enabled == true ) {
var dataset = groupsData [ groupIds [ i ] ] ;
if ( ! paths . hasOwnProperty ( groupIds [ i ] ) ) {
paths [ groupIds [ i ] ] = Lines . calcPath ( dataset , group ) ;
}
if ( group . options . shaded . orientation === "group" ) {
var subGroupId = group . options . shaded . groupId ;
if ( groupIds . indexOf ( subGroupId ) === - 1 ) {
console . log ( "Unknown shading group target given:" + subGroupId ) ;
continue ;
}
if ( ! paths . hasOwnProperty ( subGroupId ) ) {
paths [ subGroupId ] = Lines . calcPath ( groupsData [ subGroupId ] , this . groups [ subGroupId ] ) ;
}
Lines . drawShading ( paths [ groupIds [ i ] ] , group , paths [ subGroupId ] , this . framework ) ;
}
else {
Lines . drawShading ( paths [ groupIds [ i ] ] , group , undefined , this . framework ) ;
}
}
}
}
}
// draw the groups, calculating paths if still necessary.
Bars . draw ( groupIds , groupsData , this . framework ) ;
for ( i = 0 ; i < groupIds . length ; i ++ ) {
group = this . groups [ groupIds [ i ] ] ;
if ( groupsData [ groupIds [ i ] ] . length > 0 ) {
switch ( group . options . style ) {
case "line" :
if ( ! paths . hasOwnProperty ( groupIds [ i ] ) ) {
paths [ groupIds [ i ] ] = Lines . calcPath ( groupsData [ groupIds [ i ] ] , group ) ;
}
Lines . draw ( paths [ groupIds [ i ] ] , group , this . framework ) ;
//explicit no break;
case "points" :
if ( group . options . style == "points" || group . options . drawPoints . enabled == true ) {
Points . draw ( groupsData [ groupIds [ i ] ] , group , this . framework ) ;
}
break ;
case "bar" :
// bar needs to be drawn enmasse
//explicit no break
default :
//do nothing...
}
}
}
}
}
}
}
}
}
@ -638,6 +712,51 @@ LineGraph.prototype._updateGraph = function () {
return false ;
return false ;
} ;
} ;
LineGraph . prototype . _stack = function ( data , subData ) {
var index , dx , dy , subPrevPoint , subNextPoint ;
index = 0 ;
// for each data point we look for a matching on in the set below
for ( var j = 0 ; j < data . length ; j ++ ) {
subPrevPoint = undefined ;
subNextPoint = undefined ;
// we look for time matches or a before-after point
for ( var k = index ; k < subData . length ; k ++ ) {
// if times match exactly
if ( subData [ k ] . x === data [ j ] . x ) {
subPrevPoint = subData [ k ] ;
subNextPoint = subData [ k ] ;
index = k ;
break ;
}
else if ( subData [ k ] . x > data [ j ] . x ) { // overshoot
subNextPoint = subData [ k ] ;
if ( k == 0 ) {
subPrevPoint = subNextPoint ;
}
else {
subPrevPoint = subData [ k - 1 ] ;
}
index = k ;
break ;
}
}
// in case the last data point has been used, we assume it stays like this.
if ( subNextPoint === undefined ) {
subPrevPoint = subData [ subData . length - 1 ] ;
subNextPoint = subData [ subData . length - 1 ] ;
}
// linear interpolation
dx = subNextPoint . x - subPrevPoint . x ;
dy = subNextPoint . y - subPrevPoint . y ;
if ( dx == 0 ) {
data [ j ] . y = data [ j ] . orginalY + subNextPoint . y ;
}
else {
data [ j ] . y = data [ j ] . orginalY + ( dy / dx ) * ( data [ j ] . x - subPrevPoint . x ) + subPrevPoint . y ; // ax + b where b is data[j].y
}
}
}
/ * *
/ * *
* first select and preprocess the data from the datasets .
* first select and preprocess the data from the datasets .
@ -657,33 +776,24 @@ LineGraph.prototype._getRelevantData = function (groupIds, groupsData, minDate,
if ( groupIds . length > 0 ) {
if ( groupIds . length > 0 ) {
for ( i = 0 ; i < groupIds . length ; i ++ ) {
for ( i = 0 ; i < groupIds . length ; i ++ ) {
group = this . groups [ groupIds [ i ] ] ;
group = this . groups [ groupIds [ i ] ] ;
groupsData [ groupIds [ i ] ] = [ ] ;
var dataContainer = groupsData [ groupIds [ i ] ] ;
var itemsData = group . getItems ( ) ;
// optimization for sorted data
// optimization for sorted data
if ( group . options . sort == true ) {
if ( group . options . sort == true ) {
var guess = Math . max ( 0 , util . binarySearchValue ( group . itemsData , minDate , 'x' , 'before' ) ) ;
for ( j = guess ; j < group . itemsData . length ; j ++ ) {
var first = Math . max ( 0 , util . binarySearchValue ( itemsData , minDate , 'x' , 'before' ) ) ;
var last = Math . min ( itemsData . length , util . binarySearchValue ( itemsData , maxDate , 'x' , 'after' ) ) ;
if ( last < 0 ) {
last = itemsData . length ;
}
var dataContainer = new Array ( last - first ) ;
for ( j = first ; j < last ; j ++ ) {
item = group . itemsData [ j ] ;
item = group . itemsData [ j ] ;
if ( item !== undefined ) {
if ( item . x > maxDate ) {
dataContainer . push ( item ) ;
break ;
}
else {
dataContainer . push ( item ) ;
}
}
dataContainer [ j - first ] = item ;
}
}
groupsData [ groupIds [ i ] ] = dataContainer ;
}
}
else {
else {
for ( j = 0 ; j < group . itemsData . length ; j ++ ) {
item = group . itemsData [ j ] ;
if ( item !== undefined ) {
if ( item . x > minDate && item . x < maxDate ) {
dataContainer . push ( item ) ;
}
}
}
// If unsorted data, all data is relevant, just returning entire structure
groupsData [ groupIds [ i ] ] = group . itemsData ;
}
}
}
}
}
}
@ -713,12 +823,12 @@ LineGraph.prototype._applySampling = function (groupIds, groupsData) {
var pointsPerPixel = amountOfPoints / xDistance ;
var pointsPerPixel = amountOfPoints / xDistance ;
increment = Math . min ( Math . ceil ( 0.2 * amountOfPoints ) , Math . max ( 1 , Math . round ( pointsPerPixel ) ) ) ;
increment = Math . min ( Math . ceil ( 0.2 * amountOfPoints ) , Math . max ( 1 , Math . round ( pointsPerPixel ) ) ) ;
var sampledData = [ ] ;
var sampledData = new Array ( amountOfPoints ) ;
for ( var j = 0 ; j < amountOfPoints ; j += increment ) {
for ( var j = 0 ; j < amountOfPoints ; j += increment ) {
sampledData . push ( dataContainer [ j ] ) ;
var idx = Math . round ( j / increment ) ;
sampledData [ idx ] = dataContainer [ j ] ;
}
}
groupsData [ groupIds [ i ] ] = sampledData ;
groupsData [ groupIds [ i ] ] = sampledData . splice ( 0 , Math . round ( amountOfPoints / increment ) ) ;
}
}
}
}
}
}
@ -747,22 +857,22 @@ LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) {
group = this . groups [ groupIds [ i ] ] ;
group = this . groups [ groupIds [ i ] ] ;
// if bar graphs are stacked, their range need to be handled differently and accumulated over all groups.
// if bar graphs are stacked, their range need to be handled differently and accumulated over all groups.
if ( options . stack === true && options . style === 'bar' ) {
if ( options . stack === true && options . style === 'bar' ) {
if ( options . yAxisOrientation === 'left' ) { combinedDataLeft = combinedDataLeft . concat ( group . getData ( groupData ) ) ; }
else { combinedDataRight = combinedDataRight . concat ( group . getData ( groupData ) ) ; }
if ( options . yAxisOrientation === 'left' ) {
combinedDataLeft = combinedDataLeft . concat ( group . getItems ( ) ) ;
}
else {
combinedDataRight = combinedDataRight . concat ( group . getItems ( ) ) ;
}
}
}
else {
else {
groupRanges [ groupIds [ i ] ] = group . getYRange ( groupData , groupIds [ i ] ) ;
groupRanges [ groupIds [ i ] ] = group . getYRange ( groupData , groupIds [ i ] ) ;
}
}
}
}
}
}
// if bar graphs are stacked, their range need to be handled differently and accumulated over all groups.
// if bar graphs are stacked, their range need to be handled differently and accumulated over all groups.
BarFunctions . getStackedYRange ( combinedDataLeft , groupRanges , groupIds , '__barStackLeft' , 'left' ) ;
BarFunctions . getStackedYRange ( combinedDataRight , groupRanges , groupIds , '__barStackRight' , 'right' ) ;
// if line graphs are stacked, their range need to be handled differently and accumulated over all groups.
//LineFunctions.getStackedYRange(combinedDataLeft , groupRanges, groupIds, '__lineStackLeft' , 'left' );
//LineFunctions.getStackedYRange(combinedDataRight, groupRanges, groupIds, '__lineStackRight', 'right');
Bars . getStackedYRange ( combinedDataLeft , groupRanges , groupIds , '__barStackLeft' , 'left' ) ;
Bars . getStackedYRange ( combinedDataRight , groupRanges , groupIds , '__barStackRight' , 'right' ) ;
}
}
} ;
} ;
@ -823,7 +933,7 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) {
this . yAxisRight . setRange ( minRight , maxRight ) ;
this . yAxisRight . setRange ( minRight , maxRight ) ;
}
}
}
}
resized = this . _toggleAxisVisiblity ( yAxisLeftUsed , this . yAxisLeft ) || resized ;
resized = this . _toggleAxisVisiblity ( yAxisLeftUsed , this . yAxisLeft ) || resized ;
resized = this . _toggleAxisVisiblity ( yAxisRightUsed , this . yAxisRight ) || resized ;
resized = this . _toggleAxisVisiblity ( yAxisRightUsed , this . yAxisRight ) || resized ;
if ( yAxisRightUsed == true && yAxisLeftUsed == true ) {
if ( yAxisRightUsed == true && yAxisLeftUsed == true ) {
@ -836,8 +946,12 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) {
}
}
this . yAxisRight . master = ! yAxisLeftUsed ;
this . yAxisRight . master = ! yAxisLeftUsed ;
if ( this . yAxisRight . master == false ) {
if ( this . yAxisRight . master == false ) {
if ( yAxisRightUsed == true ) { this . yAxisLeft . lineOffset = this . yAxisRight . width ; }
else { this . yAxisLeft . lineOffset = 0 ; }
if ( yAxisRightUsed == true ) {
this . yAxisLeft . lineOffset = this . yAxisRight . width ;
}
else {
this . yAxisLeft . lineOffset = 0 ;
}
resized = this . yAxisLeft . redraw ( ) || resized ;
resized = this . yAxisLeft . redraw ( ) || resized ;
this . yAxisRight . stepPixels = this . yAxisLeft . stepPixels ;
this . yAxisRight . stepPixels = this . yAxisLeft . stepPixels ;
@ -850,9 +964,11 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) {
}
}
// clean the accumulated lists
// clean the accumulated lists
var tempGroups = [ '__barStackLeft' , '__barStackRight' , '__lineStackLeft' , '__lineStackRight' ] ;
var tempGroups = [ '__barStackLeft' , '__barStackRight' , '__lineStackLeft' , '__lineStackRight' ] ;
for ( var i = 0 ; i < tempGroups . length ; i ++ ) {
for ( var i = 0 ; i < tempGroups . length ; i ++ ) {
if ( groupIds . indexOf ( tempGroups [ i ] ) != - 1 ) { groupIds . splice ( groupIds . indexOf ( tempGroups [ i ] ) , 1 ) ; }
if ( groupIds . indexOf ( tempGroups [ i ] ) != - 1 ) {
groupIds . splice ( groupIds . indexOf ( tempGroups [ i ] ) , 1 ) ;
}
}
}
return resized ;
return resized ;
@ -871,7 +987,7 @@ LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) {
var changed = false ;
var changed = false ;
if ( axisUsed == false ) {
if ( axisUsed == false ) {
if ( axis . dom . frame . parentNode && axis . hidden == false ) {
if ( axis . dom . frame . parentNode && axis . hidden == false ) {
axis . hide ( )
axis . hide ( ) ;
changed = true ;
changed = true ;
}
}
}
}
@ -895,17 +1011,11 @@ LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) {
* @ private
* @ private
* /
* /
LineGraph . prototype . _convertXcoordinates = function ( datapoints ) {
LineGraph . prototype . _convertXcoordinates = function ( datapoints ) {
var extractedData = [ ] ;
var xValue , yValue ;
var toScreen = this . body . util . toScreen ;
var toScreen = this . body . util . toScreen ;
for ( var i = 0 ; i < datapoints . length ; i ++ ) {
for ( var i = 0 ; i < datapoints . length ; i ++ ) {
xValue = toScreen ( datapoints [ i ] . x ) + this . props . width ;
yValue = datapoints [ i ] . y ;
extractedData . push ( { x : xValue , y : yValue } ) ;
datapoints [ i ] . screen_x = toScreen ( datapoints [ i ] . x ) + this . props . width ;
datapoints [ i ] . screen_y = datapoints [ i ] . y ; //starting point for range calculations
}
}
return extractedData ;
} ;
} ;
@ -920,25 +1030,15 @@ LineGraph.prototype._convertXcoordinates = function (datapoints) {
* @ private
* @ private
* /
* /
LineGraph . prototype . _convertYcoordinates = function ( datapoints , group ) {
LineGraph . prototype . _convertYcoordinates = function ( datapoints , group ) {
var extractedData = [ ] ;
var xValue , yValue ;
var toScreen = this . body . util . toScreen ;
var axis = this . yAxisLeft ;
var axis = this . yAxisLeft ;
var svgHeight = Number ( this . svg . style . height . replace ( 'px' , '' ) ) ;
var svgHeight = Number ( this . svg . style . height . replace ( 'px' , '' ) ) ;
if ( group . options . yAxisOrientation == 'right' ) {
if ( group . options . yAxisOrientation == 'right' ) {
axis = this . yAxisRight ;
axis = this . yAxisRight ;
}
}
for ( var i = 0 ; i < datapoints . length ; i ++ ) {
for ( var i = 0 ; i < datapoints . length ; i ++ ) {
var labelValue = datapoints [ i ] . label ? datapoints [ i ] . label : null ;
xValue = toScreen ( datapoints [ i ] . x ) + this . props . width ;
yValue = Math . round ( axis . convertValue ( datapoints [ i ] . y ) ) ;
extractedData . push ( { x : xValue , y : yValue , label : labelValue } ) ;
datapoints [ i ] . screen_y = Math . round ( axis . convertValue ( datapoints [ i ] . y ) ) ;
}
}
group . setZeroPosition ( Math . min ( svgHeight , axis . convertValue ( 0 ) ) ) ;
group . setZeroPosition ( Math . min ( svgHeight , axis . convertValue ( 0 ) ) ) ;
return extractedData ;
} ;
} ;