1 2 3 //intialize with var data ={DATATYPE : "vq.models.StemPlotData", CONTENTS : {DATAARRAY : data_array} }; 4 // notifier = function(x,dx) where x = position within scroll bar range, dx = total length of window in scale of scroll bar 5 6 //draw with draw (data ={DATATYPE : "vq.models.StemPlotData", {DATAARRAY : data_array} } 7 // and options = {plotHeight: xx, plotWidth : xx, vertical_padding: xx, 8 // horizontal_padding : xx , maxPosition: xx, minPosition: xx, 9 // maxRange: xx, minRange: xx, dblclick_notifier : function(x,dx), 10 // fixedWindowWidth: xx, scaleMultiplier : xx, interval : xx, font : "fontname"} 11 //note dblclick_notifer is the last listed option. This can be used to create a "zoom" effect by re-instanstiating the scroll bar with new parameters. 12 13 14 vq.StemPlot = function() { 15 vq.Vis.call(this); 16 17 //set option variables to useful values before options are set. 18 this.height(500); // defaults 19 this.width(500); // defaults 20 this.vertical_padding(30); 21 this.horizontal_padding(30); 22 23 }; 24 vq.StemPlot.prototype = pv.extend(vq.Vis); 25 26 vq.StemPlot.prototype._setOptionDefaults = function(options) { 27 28 if (options.height != null) { this.height(options.height); } 29 30 if (options.width != null) { this.width(options.width); } 31 32 if (options.container) { this.container(options.container); } 33 34 if (options.vertical_padding != null) { this.vertical_padding(options.vertical_padding); } 35 36 if (options.horizontal_padding != null) { this.horizontal_padding(options.horizontal_padding); } 37 38 }; 39 40 vq.StemPlot.prototype.setXRange = function(x,dx){ 41 this.posX.domain(x, x+dx); 42 this.redraw(); 43 }; 44 45 vq.StemPlot.prototype.draw = function(data) { 46 47 this._bl_data = new vq.models.StemPlotData(data); 48 if (this._bl_data.isDataReady()) { 49 this._setOptionDefaults(this._bl_data); 50 this.render(); 51 } 52 }; 53 54 vq.StemPlot.prototype.render = function() { 55 var that = this; 56 this._data = this._bl_data._data; 57 this.visibleWidth = (this.width() - 2 * this.horizontal_padding()); 58 this.visibleHeight = (this.height() - this.vertical_padding() * 2); 59 this.context_height = 50; 60 this.focus_height = this.visibleHeight - this.context_height; 61 var x_id = that._bl_data.COLUMNID.x, y_id = that._bl_data.COLUMNID.y; 62 this.posX = pv.Scale.linear().range(0,that.visibleWidth); 63 this.posY = pv.Scale.linear().range(0,that.focus_height); 64 this.context_posX = pv.Scale.linear(0, pv.max(that._data.map(function(d){ 65 return d[x_id];}))).range(0,that.visibleWidth).nice(); 66 this.context_posY = pv.Scale.linear(0, pv.max(that._data.map(function(d){ 67 return d[y_id];}))).range(0,that.context_height).nice(); 68 69 that.window = {x:that.context_posX.range()[0]*.2,dx:(that.context_posX.range()[1] - that.context_posX.range()[0]) *.6}; 70 71 var s; 72 var x = that._bl_data.COLUMNID.x, y = that._bl_data.COLUMNID.y; 73 74 75 var dispatchEvent = function(d) { 76 notifier(d); 77 }; 78 79 var vis = new pv.Panel() 80 .width(that.width()) 81 .height(that.height()) 82 .left(that.horizontal_padding()) 83 .right(that.horizontal_padding()) 84 .bottom(that.vertical_padding()) 85 .top(that.vertical_padding()) 86 .canvas(that.container()); 87 88 var drawPanel = vis.add(pv.Panel); 89 90 var focus = drawPanel.add(pv.Panel) 91 .def("init", function() { 92 var d1 = that.context_posX.invert(that.window.x), 93 d2 = that.context_posX.invert(that.window.x + that.window.dx), 94 dd = that._data.slice(Math.max(0, pv.search.index(that._data, d1, function(d) {return d[x];} ) - 1), 95 pv.search.index(that._data, d2, function(d) { return d[x]; } ) + 1); 96 that.posX.domain(d1, d2); 97 that.posY.domain(that.context_posY.domain()); 98 return dd; 99 }) 100 .top(0) 101 .height(that.focus_height); 102 103 var plot = focus.add(pv.Panel) 104 .overflow('hidden') 105 106 // Stem plot 107 .add(pv.Bar) 108 .data(function() { return focus.init();}) 109 .left(function(t) { return that.posX(t[x]);}) 110 .height(function(t){ return that.posY(t[y]);}) 111 .bottom(1) 112 .width(.5) 113 .strokeStyle("red") 114 .fillStyle("red") 115 .anchor("top").add(pv.Label) 116 .fillStyle(null) 117 .visible(function(t) { return t[y] > pv.mean(that.posY.domain()) / 2;}) 118 .textBaseline("bottom") 119 .textAlign("right") 120 .textAngle(-Math.PI / 2) 121 .text(function(t){ return parseInt(t[y]);}); 122 123 124 var xtick = focus.add(pv.Rule) 125 .data(function() { return that.posX.ticks();} ) 126 .left(that.posX) 127 .strokeStyle("#eee"); 128 xtick.anchor("bottom").add(pv.Label) 129 .text(that.posX.tickFormat); 130 131 132 /* Y-axis ticks. */ 133 var ytick = focus.add(pv.Rule) 134 .data(function() { return that.posY.ticks(7);} ) 135 .bottom(that.posY) 136 .strokeStyle("#aaa") 137 /* Left label. */ 138 .anchor("left").add(pv.Label) 139 .text(that.posY.tickFormat); 140 141 /* Context panel (zoomed out). */ 142 var context = drawPanel.add(pv.Panel) 143 .bottom(0) 144 .height(that.context_height); 145 146 /* X-axis ticks. */ 147 context.add(pv.Rule) 148 .data(that.context_posX.ticks()) 149 .left(that.context_posX) 150 .strokeStyle("#eee") 151 .anchor("bottom").add(pv.Label) 152 .text(that.context_posX.tickFormat); 153 154 /* Y-axis ticks. */ 155 context.add(pv.Rule) 156 .bottom(0); 157 158 /* Context area chart. */ 159 context.add(pv.Bar) 160 .data(that._data) 161 .left(function(d) {return that.context_posX(d[x]);}) 162 .width(1) 163 .bottom(1) 164 .height(function(d) { return that.context_posY(d[y]);}) 165 .fillStyle("blue"); 166 167 168 /* The selectable, draggable focus region. */ 169 context.add(pv.Panel) 170 .data([that.window]) 171 .cursor("crosshair") 172 .events("all") 173 .event("mousedown", pv.Behavior.select()) 174 .event("select", focus) 175 .add(pv.Bar) 176 .left(function(d) { return d.x;}) 177 .width(function(d) {return d.dx;}) 178 .fillStyle("rgba(255, 128, 128, .4)") 179 .cursor("move") 180 .event("mousedown", pv.Behavior.drag()) 181 .event("drag", focus); 182 183 vis.render(); 184 185 }; 186 187 188 vq.models.StemPlotData = function(data) { 189 vq.models.VisData.call(this,data); 190 191 this.setDataModel(); 192 193 if (this.getDataType() == 'vq.models.StemPlotData') { 194 this._build_data(this.getContents()); 195 } else { 196 console.warn('Unrecognized JSON object. Expected vq.models.StemPlotData object.'); 197 } 198 }; 199 vq.models.StemPlotData.prototype = pv.extend(vq.models.VisData); 200 201 vq.models.StemPlotData.prototype._build_data = function(data) { 202 this._processData(data); 203 if (this._data.length) { 204 this.setDataReady(true); 205 } 206 207 }; 208 209 210 vq.models.StemPlotData.prototype.setDataModel = function () { 211 this._dataModel = [ 212 {label: 'width', id: 'PLOT.width', cast : Number, defaultValue: 400}, 213 {label: 'height', id: 'PLOT.height', cast : Number, defaultValue: 400}, 214 {label : 'container', id:'PLOT.container', optional : true}, 215 {label: 'vertical_padding', id: 'PLOT.vertical_padding', cast : Number, defaultValue: 0}, 216 {label: 'horizontal_padding', id: 'PLOT.horizontal_padding',cast : Number, defaultValue: 0}, 217 {label: 'COLUMNID.x', id: 'xcolumnid',cast : String, defaultValue : 'x'}, 218 {label: 'COLUMNID.y', id: 'ycolumnid',cast : String, defaultValue : 'y'}, 219 {label : '_data', id: 'data_array', defaultValue : [] } 220 221 ]; 222 }; 223 224