@ -33,7 +33,7 @@ function LineGraph(body, options) {
style : 'line' , // line, bar
style : 'line' , // line, bar
barChart : {
barChart : {
width : 50 ,
width : 50 ,
allowOverlap : true ,
handleOverlap : 'overlap' ,
align : 'center' // left, center, right
align : 'center' // left, center, right
} ,
} ,
catmullRom : {
catmullRom : {
@ -549,79 +549,36 @@ LineGraph.prototype.redraw = function() {
LineGraph . prototype . _updateGraph = function ( ) {
LineGraph . prototype . _updateGraph = function ( ) {
// reset the svg elements
// reset the svg elements
DOMutil . prepareElements ( this . svgElements ) ;
DOMutil . prepareElements ( this . svgElements ) ;
if ( this . width != 0 && this . itemsData != null ) {
if ( this . width != 0 && this . itemsData != null ) {
var group , groupData , preprocessedGroup , i ;
var preprocessedGroupData = [ ] ;
var processedGroupData = [ ] ;
var groupRanges = [ ] ;
var group , i ;
var preprocessedGroupData = { } ;
var processedGroupData = { } ;
var groupRanges = { } ;
var changeCalled = false ;
var changeCalled = false ;
// getting group Ids
// getting group Ids
var groupIds = [ ] ;
var groupIds = [ ] ;
for ( var groupId in this . groups ) {
for ( var groupId in this . groups ) {
if ( this . groups . hasOwnProperty ( groupId ) ) {
if ( this . groups . hasOwnProperty ( groupId ) ) {
groupIds . push ( groupId ) ;
group = this . groups [ groupId ] ;
if ( group . visible == true ) {
groupIds . push ( groupId ) ;
}
}
}
}
}
// 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 ) ;
// first select and preprocess the data from the datasets.
// the groups have their preselection of data, we now loop over this data to see
// what data we need to draw. Sorted data is much faster.
// more optimization is possible by doing the sampling before and using the binary search
// to find the end date to determine the increment.
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 = { } ;
// fill groups data
this . _getRelevantData ( groupIds , groupsData , minDate , maxDate ) ;
// we transform the X coordinates to detect collisions
for ( i = 0 ; i < groupIds . length ; i ++ ) {
for ( i = 0 ; i < groupIds . length ; i ++ ) {
group = this . groups [ groupIds [ i ] ] ;
if ( group . visible == true ) {
groupData = [ ] ;
// optimization for sorted data
if ( group . options . sort == true ) {
var guess = Math . max ( 0 , util . binarySearchGeneric ( group . itemsData , minDate , 'x' , 'before' ) ) ;
for ( var j = guess ; j < group . itemsData . length ; j ++ ) {
var item = group . itemsData [ j ] ;
if ( item !== undefined ) {
if ( item . x > maxDate ) {
groupData . push ( item ) ;
break ;
}
else {
groupData . push ( item ) ;
}
}
}
}
else {
for ( var j = 0 ; j < group . itemsData . length ; j ++ ) {
var item = group . itemsData [ j ] ;
if ( item !== undefined ) {
if ( item . x > minDate && item . x < maxDate ) {
groupData . push ( item ) ;
}
}
}
}
// preprocess, split into ranges and data
if ( groupData . length > 0 ) {
preprocessedGroup = this . _preprocessData ( groupData , group ) ;
groupRanges . push ( { min : preprocessedGroup . min , max : preprocessedGroup . max } ) ;
preprocessedGroupData . push ( preprocessedGroup . data ) ;
}
else {
groupRanges . push ( { } ) ;
preprocessedGroupData . push ( [ ] ) ;
}
}
else {
groupRanges . push ( { } ) ;
preprocessedGroupData . push ( [ ] ) ;
}
preprocessedGroupData [ groupIds [ i ] ] = this . _convertXcoordinates ( groupsData [ groupIds [ i ] ] ) ;
}
}
// now all needed data has been collected we start the processing.
this . _getYRanges ( groupIds , preprocessedGroupData , 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.
@ -632,24 +589,21 @@ LineGraph.prototype._updateGraph = function () {
return ;
return ;
}
}
// w ith the yAxis scaled correctly, use this to get the Y values of the points.
// W ith 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 . push ( this . _convertYvalues ( preprocessedGroupData [ i ] , group ) )
processedGroupData [ groupIds [ i ] ] = this . _convertYcoordinates ( groupsData [ groupIds [ i ] ] , group ) ;
}
}
// draw the groups
// draw the groups
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 . visible == true ) {
if ( group . options . style == 'line' ) {
this . _drawLineGraph ( processedGroupData [ i ] , group ) ;
}
else {
this . _drawBarGraph ( processedGroupData [ i ] , group ) ;
}
if ( group . options . style == 'line' ) {
this . _drawLineGraph ( processedGroupData [ groupIds [ i ] ] , group ) ;
}
}
}
}
this . _drawBarGraphs ( groupIds , processedGroupData ) ;
}
}
}
}
@ -657,6 +611,174 @@ LineGraph.prototype._updateGraph = function () {
DOMutil . cleanupElements ( this . svgElements ) ;
DOMutil . cleanupElements ( this . svgElements ) ;
} ;
} ;
LineGraph . prototype . _getRelevantData = function ( groupIds , groupsData , minDate , maxDate ) {
// first select and preprocess the data from the datasets.
// the groups have their preselection of data, we now loop over this data to see
// what data we need to draw. Sorted data is much faster.
// more optimization is possible by doing the sampling before and using the binary search
// to find the end date to determine the increment.
var group ;
if ( groupIds . length > 0 ) {
for ( var i = 0 ; i < groupIds . length ; i ++ ) {
group = this . groups [ groupIds [ i ] ] ;
groupsData [ groupIds [ i ] ] = [ ] ;
var dataContainer = groupsData [ groupIds [ i ] ] ;
// optimization for sorted data
if ( group . options . sort == true ) {
var guess = Math . max ( 0 , util . binarySearchGeneric ( group . itemsData , minDate , 'x' , 'before' ) ) ;
for ( var j = guess ; j < group . itemsData . length ; j ++ ) {
var item = group . itemsData [ j ] ;
if ( item !== undefined ) {
if ( item . x > maxDate ) {
dataContainer . push ( item ) ;
break ;
}
else {
dataContainer . push ( item ) ;
}
}
}
}
else {
for ( var j = 0 ; j < group . itemsData . length ; j ++ ) {
var item = group . itemsData [ j ] ;
if ( item !== undefined ) {
if ( item . x > minDate && item . x < maxDate ) {
dataContainer . push ( item ) ;
}
}
}
}
}
}
this . _applySampling ( groupIds , groupsData ) ;
} ;
LineGraph . prototype . _applySampling = function ( groupIds , groupsData ) {
var group ;
if ( groupIds . length > 0 ) {
for ( var i = 0 ; i < groupIds . length ; i ++ ) {
group = this . groups [ groupIds [ i ] ] ;
if ( group . options . sampling == true ) {
var dataContainer = groupsData [ groupIds [ i ] ] ;
var increment = 1 ;
var amountOfPoints = dataContainer . length ;
// the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop
// of width changing of the yAxis.
var xDistance = this . body . util . toGlobalScreen ( dataContainer [ dataContainer . length - 1 ] . x ) - this . body . util . toGlobalScreen ( dataContainer [ 0 ] . x ) ;
var pointsPerPixel = amountOfPoints / xDistance ;
increment = Math . min ( Math . ceil ( 0.2 * amountOfPoints ) , Math . max ( 1 , Math . round ( pointsPerPixel ) ) ) ;
var sampledData = [ ] ;
for ( var j = 0 ; j < amountOfPoints ; j += increment ) {
sampledData . push ( dataContainer [ j ] ) ;
}
groupsData [ groupIds [ i ] ] = sampledData ;
}
}
}
} ;
LineGraph . prototype . _getYRanges = function ( groupIds , groupsData , groupRanges ) {
var groupData , group ;
var barCombinedDataLeft = [ ] ;
var barCombinedDataRight = [ ] ;
var barCombinedData ;
if ( groupIds . length > 0 ) {
for ( var i = 0 ; i < groupIds . length ; i ++ ) {
groupData = groupsData [ groupIds [ i ] ] ;
group = this . groups [ groupIds [ i ] ] ;
if ( group . options . style == 'line' || group . options . barChart . handleOverlap != "stack" ) {
var yMin = groupData [ 0 ] . y ;
var yMax = groupData [ 0 ] . y ;
for ( var j = 0 ; j < groupData . length ; j ++ ) {
yMin = yMin > groupData [ j ] . y ? groupData [ j ] . y : yMin ;
yMax = yMax < groupData [ j ] . y ? groupData [ j ] . y : yMax ;
}
groupRanges [ groupIds [ i ] ] = { min : yMin , max : yMax , yAxisOrientation : group . options . yAxisOrientation } ;
}
else if ( group . options . style == 'bar' ) {
if ( group . options . yAxisOrientation == 'left' ) {
barCombinedData = barCombinedDataLeft ;
}
else {
barCombinedData = barCombinedDataRight ;
}
groupRanges [ groupIds [ i ] ] = { min : 0 , max : 0 , yAxisOrientation : group . options . yAxisOrientation , ignore : true } ;
// combine data
for ( var j = 0 ; j < groupData . length ; j ++ ) {
barCombinedData . push ( {
x : groupData [ j ] . x ,
y : groupData [ j ] . y ,
groupId : groupIds [ i ]
} ) ;
}
}
}
if ( barCombinedDataLeft . length > 0 ) {
// sort by time and by group
barCombinedDataLeft . sort ( function ( a , b ) {
if ( a . x == b . x ) {
return a . groupId - b . groupId ;
} else {
return a . x - b . x ;
}
} )
var intersections = { } ;
this . _getDataIntersections ( intersections , barCombinedDataLeft ) ;
groupRanges [ "__barchartLeft" ] = this . _getStackedBarYRange ( intersections , barCombinedDataLeft ) ;
groupRanges [ "__barchartLeft" ] . yAxisOrientation = "left" ;
groupIds . push ( "__barchartLeft" ) ;
}
if ( barCombinedDataRight . length > 0 ) {
// sort by time and by group
barCombinedDataRight . sort ( function ( a , b ) {
if ( a . x == b . x ) {
return a . groupId - b . groupId ;
} else {
return a . x - b . x ;
}
} )
var intersections = { } ;
this . _getDataIntersections ( intersections , barCombinedDataRight ) ;
groupRanges [ "__barchartRight" ] = this . _getStackedBarYRange ( intersections , barCombinedDataRight ) ;
groupRanges [ "__barchartRight" ] . yAxisOrientation = "right" ;
groupIds . push ( "__barchartRight" ) ;
}
}
} ;
LineGraph . prototype . _getStackedBarYRange = function ( intersections , combinedData ) {
var key ;
var yMin = combinedData [ 0 ] . y ;
var yMax = combinedData [ 0 ] . y ;
for ( var i = 0 ; i < combinedData . length ; i ++ ) {
key = combinedData [ i ] . x ;
if ( intersections [ key ] === undefined ) {
yMin = yMin > combinedData [ i ] . y ? combinedData [ i ] . y : yMin ;
yMax = yMax < combinedData [ i ] . y ? combinedData [ i ] . y : yMax ;
}
else {
intersections [ key ] . accumulated += combinedData [ i ] . y ;
}
}
for ( var xpos in intersections ) {
if ( intersections . hasOwnProperty ( xpos ) ) {
yMin = yMin > intersections [ xpos ] . accumulated ? intersections [ xpos ] . accumulated : yMin ;
yMax = yMax < intersections [ xpos ] . accumulated ? intersections [ xpos ] . accumulated : yMax ;
}
}
return { min : yMin , max : yMax } ;
} ;
/ * *
/ * *
* this sets the Y ranges for the Y axis . It also determines which of the axis should be shown or hidden .
* this sets the Y ranges for the Y axis . It also determines which of the axis should be shown or hidden .
* @ param { array } groupIds
* @ param { array } groupIds
@ -667,22 +789,15 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) {
var yAxisLeftUsed = false ;
var yAxisLeftUsed = false ;
var yAxisRightUsed = false ;
var yAxisRightUsed = false ;
var minLeft = 1 e9 , minRight = 1 e9 , maxLeft = - 1 e9 , maxRight = - 1 e9 , minVal , maxVal ;
var minLeft = 1 e9 , minRight = 1 e9 , maxLeft = - 1 e9 , maxRight = - 1 e9 , minVal , maxVal ;
var orientation = 'left' ;
// if groups are present
// if groups are present
if ( groupIds . length > 0 ) {
if ( groupIds . length > 0 ) {
for ( var i = 0 ; i < groupIds . length ; i ++ ) {
for ( var i = 0 ; i < groupIds . length ; i ++ ) {
orientation = 'left' ;
var group = this . groups [ groupIds [ i ] ] ;
if ( group . visible == true ) {
if ( group . options . yAxisOrientation == 'right' ) {
orientation = 'right' ;
}
if ( groupRanges [ groupIds [ i ] ] . ignore !== true ) {
minVal = groupRanges [ groupIds [ i ] ] . min ;
maxVal = groupRanges [ groupIds [ i ] ] . max ;
minVal = groupRanges [ i ] . min ;
maxVal = groupRanges [ i ] . max ;
if ( orientation == 'left' ) {
if ( groupRanges [ groupIds [ i ] ] . yAxisOrientation == 'left' ) {
yAxisLeftUsed = true ;
yAxisLeftUsed = true ;
minLeft = minLeft > minVal ? minVal : minLeft ;
minLeft = minLeft > minVal ? minVal : minLeft ;
maxLeft = maxLeft < maxVal ? maxVal : maxLeft ;
maxLeft = maxLeft < maxVal ? maxVal : maxLeft ;
@ -694,6 +809,7 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) {
}
}
}
}
}
}
if ( yAxisLeftUsed == true ) {
if ( yAxisLeftUsed == true ) {
this . yAxisLeft . setRange ( minLeft , maxLeft ) ;
this . yAxisLeft . setRange ( minLeft , maxLeft ) ;
}
}
@ -727,6 +843,15 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) {
else {
else {
changeCalled = this . yAxisRight . redraw ( ) || changeCalled ;
changeCalled = this . yAxisRight . redraw ( ) || changeCalled ;
}
}
// clean the accumulated lists
if ( groupIds . indexOf ( "__barchartLeft" ) != - 1 ) {
groupIds . splice ( groupIds . indexOf ( "__barchartLeft" ) , 1 ) ;
}
if ( groupIds . indexOf ( "__barchartRight" ) != - 1 ) {
groupIds . splice ( groupIds . indexOf ( "__barchartRight" ) , 1 ) ;
}
return changeCalled ;
return changeCalled ;
} ;
} ;
@ -761,90 +886,136 @@ LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) {
* @ param datapoints
* @ param datapoints
* @ param group
* @ param group
* /
* /
LineGraph . prototype . _drawBarGraph = function ( dataset , group ) {
if ( dataset != null ) {
if ( dataset . length > 0 ) {
var coreDistance ;
var minWidth = 0.1 * group . options . barChart . width ;
var offset = 0 ;
LineGraph . prototype . _drawBarGraphs = function ( groupIds , processedGroupData ) {
var combinedData = [ ] ;
var intersections = { } ;
var coreDistance ;
var key ;
var group ;
var i , j ;
var barPoints = 0 ;
// combine all barchart data
for ( i = 0 ; i < groupIds . length ; i ++ ) {
group = this . groups [ groupIds [ i ] ] ;
if ( group . options . style == 'bar' ) {
if ( group . visible == true ) {
for ( j = 0 ; j < processedGroupData [ groupIds [ i ] ] . length ; j ++ ) {
combinedData . push ( {
x : processedGroupData [ groupIds [ i ] ] [ j ] . x ,
y : processedGroupData [ groupIds [ i ] ] [ j ] . y ,
groupId : groupIds [ i ]
} ) ;
barPoints += 1 ;
}
}
}
}
// check for intersections
var intersections = { } ;
if ( barPoints == 0 ) { return ; }
for ( var i = 0 ; i < dataset . length ; i ++ ) {
if ( i + 1 < dataset . length ) { coreDistance = Math . abs ( dataset [ i + 1 ] . x - dataset [ i ] . x ) ; }
if ( i > 0 ) { coreDistance = Math . min ( coreDistance , Math . abs ( dataset [ i - 1 ] . x - dataset [ i ] . x ) ) ; }
if ( coreDistance == 0 ) {
if ( intersections [ dataset [ i ] . x ] === undefined ) {
intersections [ dataset [ i ] . x ] = { amount : 0 , resolved : 0 } ;
}
intersections [ dataset [ i ] . x ] . amount += 1 ;
}
// sort by time and by group
combinedData . sort ( function ( a , b ) {
if ( a . x == b . x ) {
return a . groupId - b . groupId ;
} else {
return a . x - b . x ;
}
} ) ;
// get intersections
this . _getDataIntersections ( intersections , combinedData ) ;
// plot barchart
for ( i = 0 ; i < combinedData . length ; i ++ ) {
group = this . groups [ combinedData [ i ] . groupId ] ;
var minWidth = 0.1 * group . options . barChart . width ;
key = combinedData [ i ] . x ;
var heightOffset = 0 ;
if ( intersections [ key ] === undefined ) {
if ( i + 1 < combinedData . length ) { coreDistance = Math . abs ( combinedData [ i + 1 ] . x - key ) ; }
if ( i > 0 ) { coreDistance = Math . min ( coreDistance , Math . abs ( combinedData [ i - 1 ] . x - key ) ) ; }
var drawData = this . _getSafeDrawData ( coreDistance , group , minWidth ) ;
}
else {
var nextKey = i + ( intersections [ key ] . amount - intersections [ key ] . resolved ) ;
var prevKey = i - ( intersections [ key ] . resolved + 1 ) ;
if ( nextKey < combinedData . length ) { coreDistance = Math . abs ( combinedData [ nextKey ] . x - key ) ; }
if ( prevKey > 0 ) { coreDistance = Math . min ( coreDistance , Math . abs ( combinedData [ prevKey ] . x - key ) ) ; }
var drawData = this . _getSafeDrawData ( coreDistance , group , minWidth ) ;
intersections [ key ] . resolved += 1 ;
if ( group . options . barChart . handleOverlap == 'stack' ) {
heightOffset = intersections [ key ] . accumulated ;
intersections [ key ] . accumulated += group . zeroPosition - combinedData [ i ] . y ;
}
else if ( group . options . barChart . handleOverlap == 'sideBySide' ) {
drawData . width = drawData . width / intersections [ key ] . amount ;
drawData . offset += ( intersections [ key ] . resolved ) * drawData . width - ( 0.5 * drawData . width * ( intersections [ key ] . amount + 1 ) ) ;
if ( group . options . barChart . align == 'left' ) { offset -= 0.5 * drawData . width ; }
else if ( group . options . barChart . align == 'right' ) { offset += 0.5 * drawData . width ; }
}
}
}
DOMutil . drawBar ( combinedData [ i ] . x + drawData . offset , combinedData [ i ] . y - heightOffset , drawData . width , group . zeroPosition - combinedData [ i ] . y , group . className + ' bar' , this . svgElements , this . svg ) ;
// draw points
if ( group . options . drawPoints . enabled == true ) {
DOMutil . drawPoint ( combinedData [ i ] . x + drawData . offset , combinedData [ i ] . y - heightOffset , group , this . svgElements , this . svg ) ;
}
}
} ;
// plot the bargraph
var key ;
for ( var i = 0 ; i < dataset . length ; i ++ ) {
key = dataset [ i ] . x ;
if ( intersections [ key ] === undefined ) {
if ( i + 1 < dataset . length ) { coreDistance = Math . abs ( dataset [ i + 1 ] . x - key ) ; }
if ( i > 0 ) { coreDistance = Math . min ( coreDistance , Math . abs ( dataset [ i - 1 ] . x - key ) ) ; }
var drawData = this . _getSafeDrawData ( coreDistance , group , minWidth ) ;
}
else {
var nextKey = i + ( intersections [ key ] . amount - intersections [ key ] . resolved ) ;
var prevKey = i - ( intersections [ key ] . resolved + 1 ) ;
if ( nextKey < dataset . length ) { coreDistance = Math . abs ( dataset [ nextKey ] . x - key ) ; }
if ( prevKey > 0 ) { coreDistance = Math . min ( coreDistance , Math . abs ( dataset [ prevKey ] . x - key ) ) ; }
var drawData = this . _getSafeDrawData ( coreDistance , group , minWidth ) ;
intersections [ key ] . resolved += 1 ;
if ( group . options . barChart . allowOverlap == false ) {
drawData . width = drawData . width / intersections [ key ] . amount ;
drawData . offset += ( intersections [ key ] . resolved ) * drawData . width - ( 0.5 * drawData . width * ( intersections [ key ] . amount + 1 ) ) ;
if ( group . options . barChart . align == 'left' ) { offset -= 0.5 * drawData . width ; }
else if ( group . options . barChart . align == 'right' ) { offset += 0.5 * drawData . width ; }
}
}
DOMutil . drawBar ( dataset [ i ] . x + drawData . offset , dataset [ i ] . y , drawData . width , group . zeroPosition - dataset [ i ] . y , group . className + ' bar' , this . svgElements , this . svg ) ;
// draw points
if ( group . options . drawPoints . enabled == true ) {
DOMutil . drawPoint ( dataset [ i ] . x + drawData . offset , dataset [ i ] . y , group , this . svgElements , this . svg ) ;
}
LineGraph . prototype . _getDataIntersections = function ( intersections , combinedData ) {
// get intersections
var coreDistance ;
for ( var i = 0 ; i < combinedData . length ; i ++ ) {
if ( i + 1 < combinedData . length ) {
coreDistance = Math . abs ( combinedData [ i + 1 ] . x - combinedData [ i ] . x ) ;
}
if ( i > 0 ) {
coreDistance = Math . min ( coreDistance , Math . abs ( combinedData [ i - 1 ] . x - combinedData [ i ] . x ) ) ;
}
if ( coreDistance == 0 ) {
if ( intersections [ combinedData [ i ] . x ] === undefined ) {
intersections [ combinedData [ i ] . x ] = { amount : 0 , resolved : 0 , accumulated : 0 } ;
}
}
intersections [ combinedData [ i ] . x ] . amount += 1 ;
}
}
}
}
} ;
} ;
//LineGraph.prototype._accumulate = function (intersections, combinedData) {
LineGraph . prototype . _getSafeDrawData = function ( coreDistance , group , minWidth ) {
LineGraph . prototype . _getSafeDrawData = function ( coreDistance , group , minWidth ) {
var width , offset ;
var width , offset ;
if ( coreDistance < group . options . barChart . width && coreDistance > 0 ) {
if ( coreDistance < group . options . barChart . width && coreDistance > 0 ) {
width = coreDistance < minWidth ? minWidth : coreDistance ;
width = coreDistance < minWidth ? minWidth : coreDistance ;
offset = 0 ; // recalculate offset with the new width;
offset = 0 ; // recalculate offset with the new width;
if ( group . options . slots ) { // recalculate the shared width and offset if these options are set.
width = ( width / group . options . slots . total ) ;
offset = group . options . slots . slot * width - ( 0.5 * width * ( group . options . slots . total + 1 ) ) ;
if ( group . options . barChart . align == 'left' ) {
offset -= 0.5 * coreDistance ;
}
else if ( group . options . barChart . align == 'right' ) {
offset += 0.5 * coreDistance ;
}
}
if ( group . options . barChart . align == 'left' ) { offset -= 0.5 * coreDistance ; }
else if ( group . options . barChart . align == 'right' ) { offset += 0.5 * coreDistance ; }
}
}
else {
else {
// no collisions, plot with default settings
// no collisions, plot with default settings
width = group . options . barChart . width ;
width = group . options . barChart . width ;
offset = 0 ;
offset = 0 ;
if ( group . options . slots ) {
// if the groups are sharing the same points, this allows them to be plotted side by side
width = width / group . options . slots . total ;
offset = group . options . slots . slot * width - ( 0.5 * width * ( group . options . slots . total + 1 ) ) ;
if ( group . options . barChart . align == 'left' ) {
offset -= 0.5 * group . options . barChart . width ;
}
else if ( group . options . barChart . align == 'right' ) {
offset += 0.5 * group . options . barChart . width ;
}
}
if ( group . options . barChart . align == 'left' ) { offset -= 0.5 * group . options . barChart . width ; }
else if ( group . options . barChart . align == 'right' ) { offset += 0.5 * group . options . barChart . width ; }
}
}
return { width : width , offset : offset } ;
return { width : width , offset : offset } ;
}
} ;
/ * *
/ * *
@ -919,69 +1090,52 @@ LineGraph.prototype._drawPoints = function (dataset, group, JSONcontainer, svg,
* @ returns { Array }
* @ returns { Array }
* @ private
* @ private
* /
* /
LineGraph . prototype . _preprocessData = function ( datapoints , group ) {
LineGraph . prototype . _convertXcoordinates = function ( datapoints ) {
var extractedData = [ ] ;
var extractedData = [ ] ;
var xValue , yValue ;
var xValue , yValue ;
var toScreen = this . body . util . toScreen ;
var toScreen = this . body . util . toScreen ;
var increment = 1 ;
var amountOfPoints = datapoints . length ;
var yMin = datapoints [ 0 ] . y ;
var yMax = datapoints [ 0 ] . y ;
// the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop
// of width changing of the yAxis.
if ( group . options . sampling == true ) {
var xDistance = this . body . util . toGlobalScreen ( datapoints [ datapoints . length - 1 ] . x ) - this . body . util . toGlobalScreen ( datapoints [ 0 ] . x ) ;
var pointsPerPixel = amountOfPoints / xDistance ;
increment = Math . min ( Math . ceil ( 0.2 * amountOfPoints ) , Math . max ( 1 , Math . round ( pointsPerPixel ) ) ) ;
}
for ( var i = 0 ; i < amountOfPoints ; i += increment ) {
for ( var i = 0 ; i < datapoints . length ; i ++ ) {
xValue = toScreen ( datapoints [ i ] . x ) + this . width - 1 ;
xValue = toScreen ( datapoints [ i ] . x ) + this . width - 1 ;
yValue = datapoints [ i ] . y ;
yValue = datapoints [ i ] . y ;
extractedData . push ( { x : xValue , y : yValue } ) ;
extractedData . push ( { x : xValue , y : yValue } ) ;
yMin = yMin > yValue ? yValue : yMin ;
yMax = yMax < yValue ? yValue : yMax ;
}
}
// extractedData.sort(function (a,b) {return a.x - b.x;});
return { min : yMin , max : yMax , data : extractedData } ;
return extractedData ;
} ;
} ;
/ * *
/ * *
* This uses the DataAxis object to generate the correct Y coordinate on the SVG window . It uses the
* util function toScreen to get the x coordinate from the timestamp .
* This uses the DataAxis object to generate the correct X coordinate on the SVG window . It uses the
* util function toScreen to get the x coordinate from the timestamp . It also pre - filters the data and get the minMax ranges for
* the yAxis .
*
*
* @ param datapoints
* @ param datapoints
* @ param options
* @ returns { Array }
* @ returns { Array }
* @ private
* @ private
* /
* /
LineGraph . prototype . _convertYvalu es = function ( datapoints , group ) {
LineGraph . prototype . _convertYcoordinat es = function ( datapoints , group ) {
var extractedData = [ ] ;
var extractedData = [ ] ;
var xValue , yValue ;
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 ++ ) {
xValue = datapoints [ i ] . x ;
xValue = toScreen ( datapoints [ i ] . x ) + this . width - 1 ;
yValue = Math . round ( axis . convertValue ( datapoints [ i ] . y ) ) ;
yValue = Math . round ( axis . convertValue ( datapoints [ i ] . y ) ) ;
extractedData . push ( { x : xValue , y : yValue } ) ;
extractedData . push ( { x : xValue , y : yValue } ) ;
}
}
group . setZeroPosition ( Math . min ( svgHeight , axis . convertValue ( 0 ) ) ) ;
group . setZeroPosition ( Math . min ( svgHeight , axis . convertValue ( 0 ) ) ) ;
// extractedData.sort(function (a,b) {return a.x - b.x;});
return extractedData ;
return extractedData ;
} ;
} ;
/ * *
/ * *
* This uses an uniform parametrization of the CatmullRom algorithm :
* This uses an uniform parametrization of the CatmullRom algorithm :
* "On the Parameterization of Catmull-Rom Curves" by Cem Yuksel et al .
* "On the Parameterization of Catmull-Rom Curves" by Cem Yuksel et al .