UNPKG

foam-framework

Version:
329 lines (288 loc) 8.62 kB
/** * @license * Copyright 2015 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ CLASS({ package: 'foam.graphics', name: 'Graph', extends: 'foam.graphics.CView', properties: [ { name: 'style', type: 'String', defaultValue: 'Line', // TODO: fix the view, it's not storabe view: { factory_: 'foam.ui.ChoiceView', choices: [ 'Bar', 'Line', 'Point' ] } }, { model_: 'ColorProperty', name: 'graphColor', defaultValue: 'green' }, { model_: 'ColorProperty', name: 'backgroundColor', defaultValue: undefined }, { name: 'lineWidth', defaultValue: 6 }, { model_: 'BooleanProperty', name: 'drawShadow', defaultValue: true }, { model_: 'ColorProperty', name: 'capColor', defaultValue: '' }, { model_: 'ColorProperty', name: 'axisColor', defaultValue: 'black' }, { model_: 'ColorProperty', name: 'gridColor', type: 'String', defaultValue: undefined }, { name: 'axisSize', type: 'int', defaultValue: 2 }, { name: 'xAxisInterval', type: 'int', defaultValue: 0 }, { name: 'yAxisInterval', type: 'int', defaultValue: 0 }, { name: 'maxValue', label: 'Maximum Value', type: 'float', defaultValue: -1 }, { name: 'data', type: 'Array[float]', factory: function() { return []; }, postSet: function(old, nu) { if ( old === nu ) return; if ( old ) old.unlisten(this.onDataChange); if ( nu ) nu.listen(this.onDataChange); this.onDataChange(); } }, { model_: 'FunctionProperty', name: 'f', label: 'Data Function', required: false, defaultValue: function (x) { return x; }, help: 'The graph\'s data function.' } ], issues: [ { id: 1000, severity: 'Major', status: 'Open', summary: 'Make \'style\' view serializable', created: 'Sun Dec 23 2012 18:14:56 GMT-0500 (EST)', createdBy: 'kgr', assignedTo: 'kgr', notes: 'ChoiceView factory prevents Model from being serializable.' } ], methods: { paintLineData: function(canvas, x, y, xs, w, h, maxValue) { if ( this.graphColor ) { canvas.fillStyle = this.graphColor; canvas.beginPath(); canvas.moveTo(x+xs, y+h-xs); for ( var i = 0 ; i < this.data.length ; i++ ) { var d = this.f(this.data[i]); var lx = x+xs+(i==0?0:w*i/(this.data.length-1)); var ly = this.toY(d, maxValue); canvas.lineTo(lx, ly); } canvas.lineTo(x+this.width-1, y+h-xs); canvas.lineTo(x+xs, y+h-xs); canvas.fill(); } if ( this.capColor ) { if ( this.drawShadow ) { canvas.shadowOffsetX = 0; canvas.shadowOffsetY = 2; canvas.shadowBlur = 2; canvas.shadowColor = "rgba(0, 0, 0, 0.5)"; } canvas.strokeStyle = this.capColor; canvas.lineWidth = this.lineWidth; canvas.lineJoin = 'round'; canvas.beginPath(); for ( var i = 0 ; i < this.data.length ; i++ ) { var d = this.f(this.data[i]); var lx = this.toX(i)+0.5; var ly = this.toY(d, maxValue)/*+0.5*/-5; if ( i == 0 ) canvas.moveTo(lx, ly); else canvas.lineTo(lx, ly); } canvas.stroke(); } }, paintPointData: function(canvas, x, y, xs, w, h, maxValue) { canvas.shadowOffsetX = 2; canvas.shadowOffsetY = 2; canvas.shadowBlur = 2; canvas.shadowColor = "rgba(0, 0, 0, 0.5)"; canvas.strokeStyle = this.capColor; canvas.lineWidth = 2; canvas.lineJoin = 'round'; canvas.beginPath(); for ( var i = 0 ; i < this.data.length ; i++ ) { var d = this.f(this.data[i]); var lx = this.toX(i)+0.5; var ly = this.toY(d, maxValue)+0.5; if ( i == 0 ) canvas.moveTo(lx, ly); else canvas.lineTo(lx, ly); } canvas.stroke(); canvas.lineWidth = 3; for ( var i = 0 ; i < this.data.length ; i++ ) { var d = this.f(this.data[i]); var lx = this.toX(i)+0.5; var ly = this.toY(d, maxValue)+0.5; canvas.beginPath(); canvas.arc(lx,ly,4,0,-Math.PI/2); canvas.closePath(); canvas.stroke(); } }, paintBarData: function(canvas, x, y, xs, w, h, maxValue) { canvas.fillStyle = this.graphColor; for ( var i = 0 ; i < this.data.length ; i++ ) { var d = this.f(this.data[i]); var x1 = x+xs+w*i/this.data.length; var y1 = this.toY(d, maxValue); canvas.fillRect(x1, y1, w/this.data.length+1.5, d*h/maxValue); } }, paint: function() { var canvas = this.canvas; var x = this.x; var y = this.y; var xs = this.axisSize; var w = this.width-xs; var h = this.height-xs; var maxValue = this.maxValue; if ( this.backgroundColor ) { canvas.fillStyle = this.backgroundColor; canvas.fillRect(x,y,w,h); } if ( maxValue == -1 ) { maxValue = 0.001; for ( var i = 0 ; i < this.data.length ; i++ ) { var d = this.f(this.data[i]); maxValue = Math.max(maxValue, d); } } if ( this.style == 'Line' ) this.paintLineData(canvas, x, y, xs, w, h, maxValue); else if ( this.style == 'Bar' ) this.paintBarData(canvas, x, y, xs, w, h, maxValue); else if ( this.style == 'Point' ) this.paintPointData(canvas, x, y, xs, w, h, maxValue); if ( this.axisColor && xs != 0 ) { canvas.fillStyle = this.axisColor; // x-axis canvas.fillRect(x, y+h-xs*1.5, this.width, xs); // y-axis canvas.fillRect(x, y, xs, this.height-xs*1.5); } if ( this.xAxisInterval ) { for ( var i = this.xAxisInterval ; i <= this.data.length ; i += this.xAxisInterval ) { var x2 = this.toX(i); if ( this.gridColor ) { canvas.save(); canvas.shadowOffsetX = 0; canvas.shadowOffsetY = 0; canvas.shadowBlur = 0; canvas.fillStyle = this.gridColor; canvas.fillRect(x2+1.5, this.toY(0,1)-2*xs, 0.5, -this.height); canvas.restore(); } canvas.fillRect(x2, this.toY(0,1)-2*xs, xs/2, -xs); } } if ( this.yAxisInterval ) { for ( var i = this.yAxisInterval ; i <= maxValue ; i += this.yAxisInterval ) { var y = this.toY(i, maxValue); if ( this.gridColor ) { canvas.save(); canvas.shadowOffsetX = 0; canvas.shadowOffsetY = 0; canvas.shadowBlur = 0; canvas.fillStyle = this.gridColor; canvas.fillRect(x+xs, y+3, this.width, 0.5); canvas.restore(); } canvas.fillRect(x+xs, y, xs, xs/2); } } }, toX: function(val) { var w = this.width - this.axisSize; return this.x+this.axisSize+(val==0?0:w*val/(this.data.length-1)); }, toY: function(val, maxValue) { var h = this.height - this.axisSize; return this.y+h-val*h/maxValue+0.5; }, lastValue: function() { return this.data[this.data.length-1]; }, addData: function(value, opt_maxNumValues) { var maxNumValues = opt_maxNumValues || this.width; if ( this.data.length == maxNumValues ) this.data.shift(); this.data.push(value); }, watch: function(value, opt_maxNumValues) { var graph = this; value.addListener(function() { graph.addData(value.get(), opt_maxNumValues); }); } }, listeners: [ { name: 'onDataChange', code: function() { this.view && this.view.paint(); } } ] });