foam-framework
Version:
MVC metaprogramming framework
329 lines (288 loc) • 8.62 kB
JavaScript
/**
* @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(); }
}
]
});