source: Dev/trunk/src/client/dojox/charting/plot2d/Default.js

Last change on this file was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 14.9 KB
RevLine 
[483]1define(["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/array", "dojo/has",
2                "./CartesianBase", "./_PlotEvents", "./common", "dojox/lang/functional", "dojox/lang/functional/reversed", "dojox/lang/utils", "dojox/gfx/fx"],
3        function(lang, declare, arr, has, CartesianBase, _PlotEvents, dc, df, dfr, du, fx){
4
5        /*=====
6        declare("dojox.charting.plot2d.__DefaultCtorArgs", dojox.charting.plot2d.__CartesianCtorArgs, {
7                // summary:
8                //              The arguments used for any/most plots.
9       
10                // lines: Boolean?
11                //              Whether or not to draw lines on this plot.  Defaults to true.
12                lines:   true,
13       
14                // areas: Boolean?
15                //              Whether or not to draw areas on this plot. Defaults to false.
16                areas:   false,
17       
18                // markers: Boolean?
19                //              Whether or not to draw markers at data points on this plot. Default is false.
20                markers: false,
21       
22                // tension: Number|String?
23                //              Whether or not to apply 'tensioning' to the lines on this chart.
24                //              Options include a number, "X", "x", or "S"; if a number is used, the
25                //              simpler bezier curve calculations are used to draw the lines.  If X, x or S
26                //              is used, the more accurate smoothing algorithm is used.
27                tension: "",
28       
29                // animate: Boolean?|Number?
30                //              Whether or not to animate the chart to place. When a Number it specifies the duration of the animation.
31                //              Default is false.
32                animate: false,
33       
34                // stroke: dojox.gfx.Stroke?
35                //              An optional stroke to use for any series on the plot.
36                stroke:         {},
37       
38                // outline: dojox.gfx.Stroke?
39                //              An optional stroke used to outline any series on the plot.
40                outline:        {},
41       
42                // shadow: dojox.gfx.Stroke?
43                //              An optional stroke to use to draw any shadows for a series on a plot.
44                shadow:         {},
45       
46                // fill: dojox.gfx.Fill?
47                //              Any fill to be used for elements on the plot (such as areas).
48                fill:           {},
49
50                // filter: dojox.gfx.Filter?
51                //              An SVG filter to be used for elements on the plot. gfx SVG renderer must be used and dojox/gfx/svgext must
52                //              be required for this to work.
53                filter:         {},
54
55                // styleFunc: Function?
56                //              A function that returns a styling object for the a given data item.
57                styleFunc:      null,
58       
59                // font: String?
60                //              A font definition to be used for labels and other text-based elements on the plot.
61                font:           "",
62       
63                // fontColor: String|dojo.Color?
64                //              The color to be used for any text-based elements on the plot.
65                fontColor:      "",
66       
67                // markerStroke: dojo.gfx.Stroke?
68                //              An optional stroke to use for any markers on the plot.
69                markerStroke:           {},
70       
71                // markerOutline: dojo.gfx.Stroke?
72                //              An optional outline to use for any markers on the plot.
73                markerOutline:          {},
74       
75                // markerShadow: dojo.gfx.Stroke?
76                //              An optional shadow to use for any markers on the plot.
77                markerShadow:           {},
78       
79                // markerFill: dojo.gfx.Fill?
80                //              An optional fill to use for any markers on the plot.
81                markerFill:                     {},
82       
83                // markerFont: String?
84                //              An optional font definition to use for any markers on the plot.
85                markerFont:                     "",
86       
87                // markerFontColor: String|dojo.Color?
88                //              An optional color to use for any marker text on the plot.
89                markerFontColor:        "",
90               
91                // enableCache: Boolean?
92                //              Whether the markers are cached from one rendering to another. This improves the rendering performance of
93                //              successive rendering but penalize the first rendering.  Default false.
94                enableCache: false,
95
96                // interpolate: Boolean?
97                //              Whether when there is a null data point in the data the plot interpolates it or if the lines is split at that
98                //              point.  Default false.
99                interpolate: false
100        });
101=====*/
102
103        var purgeGroup = dfr.lambda("item.purgeGroup()");
104
105        var DEFAULT_ANIMATION_LENGTH = 1200;    // in ms
106
107        return declare("dojox.charting.plot2d.Default", [CartesianBase, _PlotEvents], {
108               
109                // defaultParams:
110                //              The default parameters of this plot.
111                defaultParams: {
112                        lines:   true,  // draw lines
113                        areas:   false, // draw areas
114                        markers: false, // draw markers
115                        tension: "",    // draw curved lines (tension is "X", "x", or "S")
116                        animate: false, // animate chart to place
117                        enableCache: false,
118                        interpolate: false
119                },
120               
121                // optionalParams:
122                //              The optional parameters of this plot.
123                optionalParams: {
124                        // theme component
125                        stroke:         {},
126                        outline:        {},
127                        shadow:         {},
128                        fill:           {},
129                        filter:     {},
130                        styleFunc: null,
131                        font:           "",
132                        fontColor:      "",
133                        marker:             "",
134                        markerStroke:           {},
135                        markerOutline:          {},
136                        markerShadow:           {},
137                        markerFill:                     {},
138                        markerFont:                     "",
139                        markerFontColor:        ""
140                },
141
142                constructor: function(chart, kwArgs){
143                        // summary:
144                        //              Return a new plot.
145                        // chart: dojox/charting/Chart
146                        //              The chart this plot belongs to.
147                        // kwArgs: dojox.charting.plot2d.__DefaultCtorArgs?
148                        //              An optional arguments object to help define this plot.
149                        this.opt = lang.clone(lang.mixin(this.opt, this.defaultParams));
150                        du.updateWithObject(this.opt, kwArgs);
151                        du.updateWithPattern(this.opt, kwArgs, this.optionalParams);
152                        // animation properties
153                        this.animate = this.opt.animate;
154                },
155
156                createPath: function(run, creator, params){
157                        var path;
158                        if(this.opt.enableCache && run._pathFreePool.length > 0){
159                                path = run._pathFreePool.pop();
160                                path.setShape(params);
161                                // was cleared, add it back
162                                creator.add(path);
163                        }else{
164                                path = creator.createPath(params);
165                        }
166                        if(this.opt.enableCache){
167                                run._pathUsePool.push(path);
168                        }
169                        return path;
170                },
171
172                buildSegments: function(i, indexed){
173                        var run = this.series[i],
174                                min = indexed?Math.max(0, Math.floor(this._hScaler.bounds.from - 1)):0,
175                                max = indexed?Math.min(run.data.length, Math.ceil(this._hScaler.bounds.to)):run.data.length,
176                                rseg = null, segments = [];
177
178                        // split the run data into dense segments (each containing no nulls)
179                        // except if interpolates is false in which case ignore null between valid data
180                        for(var j = min; j < max; j++){
181                                if(run.data[j] != null && (indexed || run.data[j].y != null)){
182                                        if(!rseg){
183                                                rseg = [];
184                                                segments.push({index: j, rseg: rseg});
185                                        }
186                                        rseg.push((indexed && run.data[j].hasOwnProperty("y"))?run.data[j].y:run.data[j]);
187                                }else{
188                                        if(!this.opt.interpolate || indexed){
189                                                // we break the line only if not interpolating or if we have indexed data
190                                                rseg = null;
191                                        }
192                                }
193                        }
194                        return segments;
195                },
196
197                render: function(dim, offsets){
198                        // summary:
199                        //              Render/draw everything on this plot.
200                        // dim: Object
201                        //              An object of the form { width, height }
202                        // offsets: Object
203                        //              An object of the form { l, r, t, b }
204                        // returns: dojox/charting/plot2d/Default
205                        //              A reference to this plot for functional chaining.
206
207                        // make sure all the series is not modified
208                        if(this.zoom && !this.isDataDirty()){
209                                return this.performZoom(dim, offsets);
210                        }
211
212                        this.resetEvents();
213                        this.dirty = this.isDirty();
214                        var s;
215                        if(this.dirty){
216                                arr.forEach(this.series, purgeGroup);
217                                this._eventSeries = {};
218                                this.cleanGroup();
219                                this.getGroup().setTransform(null);
220                                s = this.getGroup();
221                                df.forEachRev(this.series, function(item){ item.cleanGroup(s); });
222                        }
223                        var t = this.chart.theme, stroke, outline, events = this.events();
224
225                        for(var i = this.series.length - 1; i >= 0; --i){
226                                var run = this.series[i];
227                                if(!this.dirty && !run.dirty){
228                                        t.skip();
229                                        this._reconnectEvents(run.name);
230                                        continue;
231                                }
232                                run.cleanGroup();
233                                if(this.opt.enableCache){
234                                        run._pathFreePool = (run._pathFreePool?run._pathFreePool:[]).concat(run._pathUsePool?run._pathUsePool:[]);
235                                        run._pathUsePool = [];
236                                }
237                                if(!run.data.length){
238                                        run.dirty = false;
239                                        t.skip();
240                                        continue;
241                                }
242
243                                var theme = t.next(this.opt.areas ? "area" : "line", [this.opt, run], true),
244                                        lpoly,
245                                        ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler),
246                                        vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler),
247                                        eventSeries = this._eventSeries[run.name] = new Array(run.data.length);
248
249                                s = run.group;
250                               
251                                // optim works only for index based case
252                                var indexed = arr.some(run.data, function(item){
253                                        return typeof item == "number" || (item && !item.hasOwnProperty("x"));
254                                });
255
256                                var rsegments = this.buildSegments(i, indexed);
257                                for(var seg = 0; seg < rsegments.length; seg++){
258                                        var rsegment = rsegments[seg];
259                                        if(indexed){
260                                                lpoly = arr.map(rsegment.rseg, function(v, i){
261                                                        return {
262                                                                x: ht(i + rsegment.index + 1) + offsets.l,
263                                                                y: dim.height - offsets.b - vt(v),
264                                                                data: v
265                                                        };
266                                                }, this);
267                                        }else{
268                                                lpoly = arr.map(rsegment.rseg, function(v){
269                                                        return {
270                                                                x: ht(v.x) + offsets.l,
271                                                                y: dim.height - offsets.b - vt(v.y),
272                                                                data: v
273                                                        };
274                                                }, this);
275                                        }
276
277                                        // if we are indexed & we interpolate we need to put all the segments as a single one now
278                                        if(indexed && this.opt.interpolate){
279                                                while(seg < rsegments.length) {
280                                                        seg++;
281                                                        rsegment = rsegments[seg];
282                                                        if(rsegment){
283                                                                lpoly = lpoly.concat(arr.map(rsegment.rseg, function(v, i){
284                                                                        return {
285                                                                                x: ht(i + rsegment.index + 1) + offsets.l,
286                                                                                y: dim.height - offsets.b - vt(v),
287                                                                                data: v
288                                                                        };
289                                                                }, this));
290                                                        }
291                                                }
292                                        }
293
294                                        var lpath = this.opt.tension ? dc.curve(lpoly, this.opt.tension) : "";
295
296                                        if(this.opt.areas && lpoly.length > 1){
297                                                var fill = this._plotFill(theme.series.fill, dim, offsets), apoly = lang.clone(lpoly);
298                                                if(this.opt.tension){
299                                                        var apath = "L" + apoly[apoly.length-1].x + "," + (dim.height - offsets.b) +
300                                                                " L" + apoly[0].x + "," + (dim.height - offsets.b) +
301                                                                " L" + apoly[0].x + "," + apoly[0].y;
302                                                        run.dyn.fill = s.createPath(lpath + " " + apath).setFill(fill).getFill();
303                                                } else {
304                                                        apoly.push({x: lpoly[lpoly.length - 1].x, y: dim.height - offsets.b});
305                                                        apoly.push({x: lpoly[0].x, y: dim.height - offsets.b});
306                                                        apoly.push(lpoly[0]);
307                                                        run.dyn.fill = s.createPolyline(apoly).setFill(fill).getFill();
308                                                }
309                                        }
310                                        if(this.opt.lines || this.opt.markers){
311                                                // need a stroke
312                                                stroke = theme.series.stroke;
313                                                if(theme.series.outline){
314                                                        outline = run.dyn.outline = dc.makeStroke(theme.series.outline);
315                                                        outline.width = 2 * outline.width + stroke.width;
316                                                }
317                                        }
318                                        if(this.opt.markers){
319                                                run.dyn.marker = theme.symbol;
320                                        }
321                                        var frontMarkers = null, outlineMarkers = null, shadowMarkers = null;
322                                        if(stroke && theme.series.shadow && lpoly.length > 1){
323                                                var shadow = theme.series.shadow,
324                                                        spoly = arr.map(lpoly, function(c){
325                                                                return {x: c.x + shadow.dx, y: c.y + shadow.dy};
326                                                        });
327                                                if(this.opt.lines){
328                                                        if(this.opt.tension){
329                                                                run.dyn.shadow = s.createPath(dc.curve(spoly, this.opt.tension)).setStroke(shadow).getStroke();
330                                                        } else {
331                                                                run.dyn.shadow = s.createPolyline(spoly).setStroke(shadow).getStroke();
332                                                        }
333                                                }
334                                                if(this.opt.markers && theme.marker.shadow){
335                                                        shadow = theme.marker.shadow;
336                                                        shadowMarkers = arr.map(spoly, function(c){
337                                                                return this.createPath(run, s, "M" + c.x + " " + c.y + " " + theme.symbol).
338                                                                        setStroke(shadow).setFill(shadow.color);
339                                                        }, this);
340                                                }
341                                        }
342                                        if(this.opt.lines && lpoly.length > 1){
343                                                var shape;
344                                                if(outline){
345                                                        if(this.opt.tension){
346                                                                run.dyn.outline = s.createPath(lpath).setStroke(outline).getStroke();
347                                                        } else {
348                                                                run.dyn.outline = s.createPolyline(lpoly).setStroke(outline).getStroke();
349                                                        }
350                                                }
351                                                if(this.opt.tension){
352                                                        run.dyn.stroke = (shape = s.createPath(lpath)).setStroke(stroke).getStroke();
353                                                } else {
354                                                        run.dyn.stroke = (shape = s.createPolyline(lpoly)).setStroke(stroke).getStroke();
355                                                }
356                                                if(shape.setFilter && theme.series.filter){
357                                                        shape.setFilter(theme.series.filter);
358                                                }
359                                        }
360                                        var markerBox = null;
361                                        if(this.opt.markers){
362                                                var markerTheme = theme;
363                                                frontMarkers = new Array(lpoly.length);
364                                                outlineMarkers = new Array(lpoly.length);
365                                                outline = null;
366                                                if(markerTheme.marker.outline){
367                                                        outline = dc.makeStroke(markerTheme.marker.outline);
368                                                        outline.width = 2 * outline.width + (markerTheme.marker.stroke ? markerTheme.marker.stroke.width : 0);
369                                                }
370                                                arr.forEach(lpoly, function(c, i){
371                                                        if(this.opt.styleFunc || typeof c.data != "number"){
372                                                                var tMixin = typeof c.data != "number" ? [c.data] : [];
373                                                                if(this.opt.styleFunc){
374                                                                        tMixin.push(this.opt.styleFunc(c.data));
375                                                                }
376                                                                markerTheme = t.addMixin(theme, "marker", tMixin, true);
377                                                        }else{
378                                                                markerTheme = t.post(theme, "marker");
379                                                        }
380                                                        var path = "M" + c.x + " " + c.y + " " + markerTheme.symbol;
381                                                        if(outline){
382                                                                outlineMarkers[i] = this.createPath(run, s, path).setStroke(outline);
383                                                        }
384                                                        frontMarkers[i] = this.createPath(run, s, path).setStroke(markerTheme.marker.stroke).setFill(markerTheme.marker.fill);
385                                                }, this);
386                                                run.dyn.markerFill = markerTheme.marker.fill;
387                                                run.dyn.markerStroke = markerTheme.marker.stroke;
388                                                if(!markerBox && this.opt.labels){
389                                                        markerBox = frontMarkers[0].getBoundingBox();
390                                                }
391                                                if(events){
392                                                        arr.forEach(frontMarkers, function(s, i){
393                                                                var o = {
394                                                                        element: "marker",
395                                                                        index:   i + rsegment.index,
396                                                                        run:     run,
397                                                                        shape:   s,
398                                                                        outline: outlineMarkers[i] || null,
399                                                                        shadow:  shadowMarkers && shadowMarkers[i] || null,
400                                                                        cx:      lpoly[i].x,
401                                                                        cy:      lpoly[i].y
402                                                                };
403                                                                if(indexed){
404                                                                        o.x = i + rsegment.index + 1;
405                                                                        o.y = run.data[i + rsegment.index];
406                                                                }else{
407                                                                        o.x = rsegment.rseg[i].x;
408                                                                        o.y = run.data[i + rsegment.index].y;
409                                                                }
410                                                                this._connectEvents(o);
411                                                                eventSeries[i + rsegment.index] = o;
412                                                        }, this);
413                                                }else{
414                                                        delete this._eventSeries[run.name];
415                                                }
416                                        }
417                                        if(this.opt.labels){
418                                                var labelBoxW = markerBox?markerBox.width:2;
419                                                var labelBoxH = markerBox?markerBox.height:2;
420                                                arr.forEach(lpoly, function(c, i){
421                                                        if(this.opt.styleFunc || typeof c.data != "number"){
422                                                                var tMixin = typeof c.data != "number" ? [c.data] : [];
423                                                                if(this.opt.styleFunc){
424                                                                        tMixin.push(this.opt.styleFunc(c.data));
425                                                                }
426                                                                markerTheme = t.addMixin(theme, "marker", tMixin, true);
427                                                        }else{
428                                                                markerTheme = t.post(theme, "marker");
429                                                        }
430                                                        this.createLabel(s, rsegment.rseg[i], { x: c.x - labelBoxW / 2, y: c.y - labelBoxH / 2,
431                                                                width: labelBoxW , height: labelBoxH }, markerTheme);
432                                                }, this);
433                                        }
434                                }
435                                run.dirty = false;
436                        }
437                        // chart mirroring starts
438                        if(has("dojo-bidi")){
439                                this._checkOrientation(this.group, dim, offsets);
440                        }
441                        // chart mirroring ends
442                        if(this.animate){
443                                // grow from the bottom
444                                var plotGroup = this.getGroup();
445                                fx.animateTransform(lang.delegate({
446                                        shape: plotGroup,
447                                        duration: DEFAULT_ANIMATION_LENGTH,
448                                        transform:[
449                                                {name:"translate", start: [0, dim.height - offsets.b], end: [0, 0]},
450                                                {name:"scale", start: [1, 0], end:[1, 1]},
451                                                {name:"original"}
452                                        ]
453                                }, this.animate)).play();
454                        }
455                        this.dirty = false;
456                        return this;    //      dojox/charting/plot2d/Default
457                }
458        });
459});
Note: See TracBrowser for help on using the repository browser.