Changeset 13715

Show
Ignore:
Timestamp:
05/13/08 00:55:02 (2 months ago)
Author:
elazutkin
Message:

dojox.charting: major refactor (see below)
- splitting geometry and rendering procedures in Chart2D,
- adding windowing to axes,
- refactoring the linear scaler,
- adding a "primitive" scaler,
- updating all plots to use the new scalers
!strict

Location:
dojox/trunk/charting
Files:
1 added
15 modified

Legend:

Unmodified
Added
Removed
  • dojox/trunk/charting/axis2d/Default.js

    r13714 r13715  
    7070                        return "scaler" in this && !(this.dirty && this.dependOnData()); 
    7171                }, 
     72                setWindow: function(scale, offset){ 
     73                        this.scale  = scale; 
     74                        this.offset = offset; 
     75                        return this.clear(); 
     76                }, 
    7277                calculate: function(min, max, span, labels){ 
    7378                        if(this.initialized()){ return this; } 
    74                         this.min = min; 
    75                         this.max = max; 
    7679                        this.labels = "labels" in this.opt ? this.opt.labels : labels; 
    7780                        this.scaler = lin.buildScaler(min, max, span, this.opt); 
    78                         var minMinorStep = 0, ta = this.chart.theme.axis,  
     81                        if("scale" in this){ 
     82                                // calculate new range 
     83                                this.opt.from = this.scaler.bounds.lower + this.offset; 
     84                                this.opt.to   = (this.scaler.bounds.upper - this.scaler.bounds.lower) / scale + this.opt.from; 
     85                                // make sure that bounds are correct 
     86                                if(isInfinite(this.opt.from) || isNaN(this.opt.from) || isInfinite(this.opt.to) || isNaN(this.opt.to) || 
     87                                                this.opt.to - this.from.to >= this.scaler.bounds.upper - this.scaler.bounds.lower){ 
     88                                        // any error --- remove from/to bounds 
     89                                        delete this.opt.from; 
     90                                        delete this.opt.to; 
     91                                        delete this.scale; 
     92                                        delete this.offset; 
     93                                }else{ 
     94                                        // shift the window, if we are out of bounds 
     95                                        if(this.opt.from < this.scaler.bounds.lower){ 
     96                                                this.opt.to   += this.scaler.bounds.lower - this.opt.from; 
     97                                                this.opt.from  = this.scaler.bounds.lower; 
     98                                        }else if(this.opt.to > this.scaler.bounds.upper){ 
     99                                                this.opt.from += this.scaler.bounds.upper - this.opt.to; 
     100                                                this.opt.to    = this.scaler.bounds.upper; 
     101                                        } 
     102                                        // update the offset 
     103                                        this.offset = this.opt.from - this.scaler.bounds.lower; 
     104                                } 
     105                                // re-calculate the scaler 
     106                                this.scaler = lin.buildScaler(min, max, span, this.opt); 
     107                        } 
     108                        var minMinorStep = 0, ta = this.chart.theme.axis, 
    79109                                taFont = "font" in this.opt ? this.opt.font : ta.font, 
    80110                                size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0; 
  • dojox/trunk/charting/Chart2D.js

    r13563 r13715  
    9393                        this.dirty = true; 
    9494                        return this; 
     95                }, 
     96                getAxis: function(name){ 
     97                        return this.axes[name]; 
    9598                }, 
    9699                addPlot: function(name, kwArgs){ 
     
    187190                        return this.render(); 
    188191                }, 
    189                 render: function(){ 
     192                getGeometry: function(){ 
     193                        var ret = {}; 
     194                        df.forIn(this.axes, function(axis){ 
     195                                if(axis.initialized()){ 
     196                                        ret[axis.name] = { 
     197                                                name:           axis.name, 
     198                                                vertical:       axis.vertical, 
     199                                                scaler:         axis.scaler, 
     200                                                ticks:          axis.ticks 
     201                                        }; 
     202                                } 
     203                        }); 
     204                        return ret; 
     205                }, 
     206                setAxisWindow: function(name, scale, offset){ 
     207                        var axis = this.axes[name]; 
     208                        if(axis){ 
     209                                axis.setWindow(scale, offset); 
     210                        } 
     211                        return this; 
     212                }, 
     213                setWindow: function(sx, sy, dx, dy){ 
     214                        if(!("plotArea" in this)){ 
     215                                this.calculateGeometry(); 
     216                        } 
     217                        df.forIn(this.axes, function(axis){ 
     218                                var scale, offset, bounds = axis.scaler.bounds, 
     219                                        s = bounds.span / (bounds.upper - bounds.lower); 
     220                                if(axis.vertical){ 
     221                                        scale  = sy; 
     222                                        offset = ("offset" in axis ? axis.offset : 0) + dy / s / scale; 
     223                                }else{ 
     224                                        scale  = sx; 
     225                                        offset = ("offset" in axis ? axis.offset : 0) + dx / s / scale; 
     226                                } 
     227                                axis.setWindow(scale, offset); 
     228                        }); 
     229                        return this; 
     230                }, 
     231                calculateGeometry: function(){ 
    190232                        if(this.dirty){ 
    191                                 return this.fullRender(); 
     233                                return this.fullGeometry(); 
    192234                        } 
    193235 
     
    199241                                } 
    200242                        }, this); 
    201  
    202                         // go over the stack backwards 
    203                         df.forEachRev(this.stack, function(plot){ plot.render(this.dim, this.offsets); }, this); 
    204  
    205                         // go over axes 
    206                         df.forIn(this.axes, function(axis){ axis.render(this.dim, this.offsets); }, this); 
    207  
    208                         this._makeClean(); 
    209  
    210                         // BEGIN FOR HTML CANVAS  
    211                         if(this.surface.render){ this.surface.render(); }; 
    212                         // END FOR HTML CANVAS 
    213  
    214                         return this; 
    215                 }, 
    216                 fullRender: function(){ 
     243                         
     244                        return this; 
     245                }, 
     246                fullGeometry: function(){ 
    217247                        this._makeDirty(); 
    218248 
    219249                        // clear old values 
    220250                        dojo.forEach(this.stack,  clear); 
    221                         dojo.forEach(this.series, purge); 
    222                         df.forIn(this.axes, purge); 
    223                         dojo.forEach(this.stack,  purge); 
    224                         this.surface.clear(); 
    225251 
    226252                        // rebuild new connections, and add defaults 
     253 
     254                        // set up a theme 
     255                        if(!this.theme){ 
     256                                this.theme = new dojox.charting.Theme(dojox.charting._def); 
     257                        } 
    227258 
    228259                        // assign series 
     
    245276                                } 
    246277                        }, this); 
    247                         // set up a theme 
    248                         if(!this.theme){ 
    249                                 this.theme = new dojox.charting.Theme(dojox.charting._def); 
    250                         } 
    251                         var requiredColors = df.foldl(this.stack, "z + plot.getRequiredColors()", 0); 
    252                         this.theme.defineColors({num: requiredColors, cache: false}); 
    253278 
    254279                        // calculate geometry 
     
    273298                        df.forIn(this.axes, clear); 
    274299                        dojo.forEach(this.stack, function(plot){ plot.calculateAxes(this.plotArea); }, this); 
     300                         
     301                        return this; 
     302                }, 
     303                render: function(){ 
     304                        if(this.dirty){ 
     305                                return this.fullRender(); 
     306                        } 
     307                         
     308                        this.calculateGeometry(); 
     309 
     310                        // go over the stack backwards 
     311                        df.forEachRev(this.stack, function(plot){ plot.render(this.dim, this.offsets); }, this); 
     312 
     313                        // go over axes 
     314                        df.forIn(this.axes, function(axis){ axis.render(this.dim, this.offsets); }, this); 
     315 
     316                        this._makeClean(); 
     317 
     318                        // BEGIN FOR HTML CANVAS 
     319                        if(this.surface.render){ this.surface.render(); }; 
     320                        // END FOR HTML CANVAS 
     321 
     322                        return this; 
     323                }, 
     324                fullRender: function(){ 
     325                        // calculate geometry 
     326                        this.fullGeometry(); 
     327                        var offsets = this.offsets, dim = this.dim; 
     328 
     329                        // get required colors 
     330                        var requiredColors = df.foldl(this.stack, "z + plot.getRequiredColors()", 0); 
     331                        this.theme.defineColors({num: requiredColors, cache: false}); 
     332 
     333                        // clear old shapes 
     334                        dojo.forEach(this.series, purge); 
     335                        df.forIn(this.axes, purge); 
     336                        dojo.forEach(this.stack,  purge); 
     337                        this.surface.clear(); 
    275338 
    276339                        // generate shapes 
     
    305368                                        this.surface.createRect({ 
    306369                                                width:  offsets.l, 
    307                                                 height: dim.height 
     370                                                height: dim.height + 1 
    308371                                        }).setFill(fill); 
    309372                                } 
     
    311374                                        this.surface.createRect({ 
    312375                                                x: dim.width - offsets.r, 
    313                                                 width:  offsets.r, 
    314                                                 height: dim.height 
     376                                                width:  offsets.r + 1, 
     377                                                height: dim.height + 1 
    315378                                        }).setFill(fill); 
    316379                                } 
    317380                                if(offsets.t){  // top 
    318381                                        this.surface.createRect({ 
    319                                                 width:  dim.width, 
     382                                                width:  dim.width + 1, 
    320383                                                height: offsets.t 
    321384                                        }).setFill(fill); 
     
    324387                                        this.surface.createRect({ 
    325388                                                y: dim.height - offsets.b, 
    326                                                 width:  dim.width, 
    327                                                 height: offsets.b 
     389                                                width:  dim.width + 1, 
     390                                                height: offsets.b + 2 
    328391                                        }).setFill(fill); 
    329392                                } 
     
    340403 
    341404                        this._makeClean(); 
     405 
     406                        // BEGIN FOR HTML CANVAS 
     407                        if(this.surface.render){ this.surface.render(); }; 
     408                        // END FOR HTML CANVAS 
    342409 
    343410                        return this; 
  • dojox/trunk/charting/plot2d/Bars.js

    r11761 r13715  
    4747                        } 
    4848                        var t = this.chart.theme, color, stroke, fill, f, 
    49                                 gap = this.opt.gap < this._vScaler.scale / 3 ? this.opt.gap : 0; 
     49                                ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), 
     50                                vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler); 
     51                                gap = this.opt.gap < this._vScaler.bounds.scale / 3 ? this.opt.gap : 0, 
     52                                baseline = Math.max(0, this._hScaler.bounds.lower), 
     53                                baselineWidth = ht(baseline), 
     54                                height = this._vScaler.bounds.scale - 2 * gap; 
    5055                        for(var i = this.series.length - 1; i >= 0; --i){ 
    5156                                var run = this.series[i]; 
     
    5964                                stroke = run.stroke ? run.stroke : dc.augmentStroke(t.series.stroke, color); 
    6065                                fill = run.fill ? run.fill : dc.augmentFill(t.series.fill, color); 
    61                                 var baseline = Math.max(0, this._hScaler.bounds.lower), 
    62                                         xoff = offsets.l + this._hScaler.scale * (baseline - this._hScaler.bounds.lower), 
    63                                         yoff = dim.height - offsets.b - this._vScaler.scale * (1.5 - this._vScaler.bounds.lower) + gap; 
    6466                                for(var j = 0; j < run.data.length; ++j){ 
    6567                                        var v = run.data[j], 
    66                                                 width  = this._hScaler.scale * (v - baseline), 
    67                                                 height = this._vScaler.scale - 2 * gap, 
     68                                                hv = ht(v), 
     69                                                width = hv - baselineWidth, 
    6870                                                w = Math.abs(width); 
    6971                                        if(w >= 1 && height >= 1){ 
    7072                                                var shape = s.createRect({ 
    71                                                         x: xoff + (width < 0 ? width : 0), 
    72                                                         y: yoff - this._vScaler.scale * j, 
     73                                                        x: offsets.l + (v < baseline ? hv : baselineWidth), 
     74                                                        y: dim.height - offsets.b - vt(j + 1.5) + gap, 
    7375                                                        width: w, height: height 
    7476                                                }).setFill(fill).setStroke(stroke); 
  • dojox/trunk/charting/plot2d/Base.js

    r11315 r13715  
    11dojo.provide("dojox.charting.plot2d.Base"); 
    22 
     3dojo.require("dojox.charting.scaler.primitive"); 
    34dojo.require("dojox.charting.Element"); 
    45dojo.require("dojox.charting.plot2d.common"); 
     
    4142                        this._hScaler = this._hAxis.getScaler(); 
    4243                }else{ 
    43                         this._hScaler = {bounds: {lower: stats.hmin, upper: stats.hmax},  
    44                                 scale: dim.width / (stats.hmax - stats.hmin)}; 
     44                        this._hScaler = dojox.charting.scaler.primitive.buildScaler(stats.hmin, stats.hmax, dim.width); 
    4545                } 
    4646                if(this._vAxis){ 
     
    5050                        this._vScaler = this._vAxis.getScaler(); 
    5151                }else{ 
    52                         this._vScaler = {bounds: {lower: stats.vmin, upper: stats.vmax},  
    53                                 scale: dim.height / (stats.vmax - stats.vmin)}; 
     52                        this._vScaler = dojox.charting.scaler.primitive.buildScaler(stats.vmin, stats.vmax, dim.height); 
    5453                } 
    5554        } 
  • dojox/trunk/charting/plot2d/Bubble.js

    r13647 r13715  
    3939                                } 
    4040                                 
    41                                 var s = run.group, points = dojo.map(run.data, function(v, i){ 
    42                                         return { 
    43                                                 x: this._hScaler.scale * (v.x - this._hScaler.bounds.lower) + offsets.l, 
    44                                                 y: dim.height - offsets.b - this._vScaler.scale * (v.y - this._vScaler.bounds.lower), 
    45                                                 radius: this._vScaler.scale * (v.size / 2) 
    46                                         }; 
    47                                 }, this); 
     41                                var s = run.group, 
     42                                        ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), 
     43                                        vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler); 
     44                                        points = dojo.map(run.data, function(v, i){ 
     45                                                return { 
     46                                                        x: ht(v.x) + offsets.l, 
     47                                                        y: dim.height - offsets.b - vt(v.y), 
     48                                                        radius: this._vScaler.bounds.scale * (v.size / 2) 
     49                                                }; 
     50                                        }, this); 
    4851 
    4952                                if(run.fill){ 
  • dojox/trunk/charting/plot2d/ClusteredBars.js

    r11761 r13715  
    2020                        } 
    2121                        var t = this.chart.theme, color, stroke, fill, f, 
    22                                 gap = this.opt.gap < this._vScaler.scale / 3 ? this.opt.gap : 0, 
    23                                 thickness = (this._vScaler.scale - 2 * gap) / this.series.length; 
     22                                ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), 
     23                                vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler); 
     24                                gap = this.opt.gap < this._vScaler.bounds.scale / 3 ? this.opt.gap : 0, 
     25                                thickness = (this._vScaler.bounds.scale - 2 * gap) / this.series.length, 
     26                                baseline = Math.max(0, this._hScaler.bounds.lower), 
     27                                baselineWidth = ht(baseline), 
     28                                height = thickness; 
    2429                        for(var i = this.series.length - 1; i >= 0; --i){ 
    25                                 var run = this.series[i]; 
     30                                var run = this.series[i], shift = thickness * (this.series.length - i - 1); 
    2631                                if(!this.dirty && !run.dirty){ continue; } 
    2732                                run.cleanGroup(); 
     
    3338                                stroke = run.stroke ? run.stroke : dc.augmentStroke(t.series.stroke, color); 
    3439                                fill = run.fill ? run.fill : dc.augmentFill(t.series.fill, color); 
    35                                 var baseline = Math.max(0, this._hScaler.bounds.lower), 
    36                                         xoff = offsets.l + this._hScaler.scale * (baseline - this._hScaler.bounds.lower), 
    37                                         yoff = dim.height - offsets.b - this._vScaler.scale * (1.5 - this._vScaler.bounds.lower) +  
    38                                                 gap + thickness * (this.series.length - i - 1); 
    3940                                for(var j = 0; j < run.data.length; ++j){ 
    4041                                        var v = run.data[j], 
    41                                                 width  = this._hScaler.scale * (v - baseline), 
    42                                                 height = thickness, w = Math.abs(width); 
     42                                                hv = ht(v), 
     43                                                width = hv - baselineWidth, 
     44                                                w = Math.abs(width); 
    4345                                        if(w >= 1 && height >= 1){ 
    4446                                                var shape = s.createRect({ 
    45                                                         x: xoff + (width < 0 ? width : 0), 
    46                                                         y: yoff - this._vScaler.scale * j, 
     47                                                        x: offsets.l + (v < baseline ? hv : baselineWidth), 
     48                                                        y: dim.height - offsets.b - vt(j + 1.5) + gap + shift, 
    4749                                                        width: w, height: height 
    4850                                                }).setFill(fill).setStroke(stroke); 
  • dojox/trunk/charting/plot2d/ClusteredColumns.js

    r11761 r13715  
    2020                        } 
    2121                        var t = this.chart.theme, color, stroke, fill, f, 
    22                                 gap = this.opt.gap < this._hScaler.scale / 3 ? this.opt.gap : 0, 
    23                                 thickness = (this._hScaler.scale - 2 * gap) / this.series.length; 
     22                                ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), 
     23                                vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler), 
     24                                gap = this.opt.gap < this._hScaler.bounds.scale / 3 ? this.opt.gap : 0, 
     25                                thickness = (this._hScaler.bounds.scale - 2 * gap) / this.series.length, 
     26                                baseline = Math.max(0, this._vScaler.bounds.lower), 
     27                                baselineHeight = vt(baseline), 
     28                                width = thickness; 
    2429                        for(var i = 0; i < this.series.length; ++i){ 
    25                                 var run = this.series[i]; 
     30                                var run = this.series[i], shift = thickness * i; 
    2631                                if(!this.dirty && !run.dirty){ continue; } 
    2732                                run.cleanGroup(); 
     
    3338                                stroke = run.stroke ? run.stroke : dc.augmentStroke(t.series.stroke, color); 
    3439                                fill = run.fill ? run.fill : dc.augmentFill(t.series.fill, color); 
    35                                 var baseline = Math.max(0, this._vScaler.bounds.lower), 
    36                                         xoff = offsets.l + this._hScaler.scale * (0.5 - this._hScaler.bounds.lower) + gap + thickness * i, 
    37                                         yoff = dim.height - offsets.b - this._vScaler.scale * (baseline - this._vScaler.bounds.lower); 
    3840                                for(var j = 0; j < run.data.length; ++j){ 
    3941                                        var v = run.data[j], 
    40                                                 width  = thickness, 
    41                                                 height = this._vScaler.scale * (v - baseline), 
     42                                                vv = vt(v), 
     43                                                height = vv - baselineHeight, 
    4244                                                h = Math.abs(height); 
    4345                                        if(width >= 1 && h >= 1){ 
    4446                                                var shape = s.createRect({ 
    45                                                         x: xoff + this._hScaler.scale * j, 
    46                                                         y: yoff - (height < 0 ? 0 : height), 
     47                                                        x: offsets.l + ht(j + 0.5) + gap + shift, 
     48                                                        y: dim.height - offsets.b - (v > baseline ? vv : baselineHeight), 
    4749                                                        width: width, height: h 
    4850                                                }).setFill(fill).setStroke(stroke); 
  • dojox/trunk/charting/plot2d/Columns.js

    r11761 r13715  
    4545                        } 
    4646                        var t = this.chart.theme, color, stroke, fill, f, 
    47                                 gap = this.opt.gap < this._hScaler.scale / 3 ? this.opt.gap : 0; 
     47                                ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), 
     48                                vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler), 
     49                                gap = this.opt.gap < this._hScaler.bounds.scale / 3 ? this.opt.gap : 0, 
     50                                baseline = Math.max(0, this._vScaler.bounds.lower), 
     51                                baselineHeight = vt(baseline), 
     52                                xoff = offsets.l + this._hScaler.bounds.scale * (0.5 - this._hScaler.bounds.lower) + gap, 
     53                                yoff = dim.height - offsets.b - this._vScaler.bounds.scale * (baseline - this._vScaler.bounds.lower), 
     54                                width  = this._hScaler.bounds.scale - 2 * gap; 
    4855                        for(var i = this.series.length - 1; i >= 0; --i){ 
    4956                                var run = this.series[i]; 
     
    5764                                stroke = run.stroke ? run.stroke : dc.augmentStroke(t.series.stroke, color); 
    5865                                fill = run.fill ? run.fill : dc.augmentFill(t.series.fill, color); 
    59                                 var baseline = Math.max(0, this._vScaler.bounds.lower), 
    60                                         xoff = offsets.l + this._hScaler.scale * (0.5 - this._hScaler.bounds.lower) + gap, 
    61                                         yoff = dim.height - offsets.b - this._vScaler.scale * (baseline - this._vScaler.bounds.lower); 
    6266                                for(var j = 0; j < run.data.length; ++j){ 
    6367