source: Dev/branches/jQueryUI/client/d3/d3.chart.js @ 249

Last change on this file since 249 was 249, checked in by hendrikvanantwerpen, 13 years ago

This one's for Subversion, because it's so close...

First widget (stripped down sequencer).
Seperated client and server code in two direcotry trees.

File size: 27.3 KB
RevLine 
[249]1(function(){d3.chart = {};
2// Inspired by http://informationandvisualization.de/blog/box-plot
3d3.chart.box = function() {
4  var width = 1,
5      height = 1,
6      duration = 0,
7      domain = null,
8      value = Number,
9      whiskers = d3_chart_boxWhiskers,
10      quartiles = d3_chart_boxQuartiles,
11      tickFormat = null;
12
13  // For each small multiple

14  function box(g) {
15    g.each(function(d, i) {
16      d = d.map(value).sort(d3.ascending);
17      var g = d3.select(this),
18          n = d.length,
19          min = d[0],
20          max = d[n - 1];
21
22      // Compute quartiles. Must return exactly 3 elements.
23      var quartileData = d.quartiles = quartiles(d);
24
25      // Compute whiskers. Must return exactly 2 elements, or null.
26      var whiskerIndices = whiskers && whiskers.call(this, d, i),
27          whiskerData = whiskerIndices && whiskerIndices.map(function(i) { return d[i]; });
28
29      // Compute outliers. If no whiskers are specified, all data are "outliers".
30      // We compute the outliers as indices, so that we can join across transitions!
31      var outlierIndices = whiskerIndices
32          ? d3.range(0, whiskerIndices[0]).concat(d3.range(whiskerIndices[1] + 1, n))
33          : d3.range(n);
34
35      // Compute the new x-scale.
36      var x1 = d3.scale.linear()
37          .domain(domain && domain.call(this, d, i) || [min, max])
38          .range([height, 0]);
39
40      // Retrieve the old x-scale, if this is an update.
41      var x0 = this.__chart__ || d3.scale.linear()
42          .domain([0, Infinity])
43          .range(x1.range());
44
45      // Stash the new scale.
46      this.__chart__ = x1;
47
48      // Note: the box, median, and box tick elements are fixed in number,
49      // so we only have to handle enter and update. In contrast, the outliers
50      // and other elements are variable, so we need to exit them! Variable
51      // elements also fade in and out.
52
53      // Update center line: the vertical line spanning the whiskers.
54      var center = g.selectAll("line.center")
55          .data(whiskerData ? [whiskerData] : []);
56
57      center.enter().insert("svg:line", "rect")
58          .attr("class", "center")
59          .attr("x1", width / 2)
60          .attr("y1", function(d) { return x0(d[0]); })
61          .attr("x2", width / 2)
62          .attr("y2", function(d) { return x0(d[1]); })
63          .style("opacity", 1e-6)
64        .transition()
65          .duration(duration)
66          .style("opacity", 1)
67          .attr("y1", function(d) { return x1(d[0]); })
68          .attr("y2", function(d) { return x1(d[1]); });
69
70      center.transition()
71          .duration(duration)
72          .style("opacity", 1)
73          .attr("y1", function(d) { return x1(d[0]); })
74          .attr("y2", function(d) { return x1(d[1]); });
75
76      center.exit().transition()
77          .duration(duration)
78          .style("opacity", 1e-6)
79          .attr("y1", function(d) { return x1(d[0]); })
80          .attr("y2", function(d) { return x1(d[1]); })
81          .remove();
82
83      // Update innerquartile box.
84      var box = g.selectAll("rect.box")
85          .data([quartileData]);
86
87      box.enter().append("svg:rect")
88          .attr("class", "box")
89          .attr("x", 0)
90          .attr("y", function(d) { return x0(d[2]); })
91          .attr("width", width)
92          .attr("height", function(d) { return x0(d[0]) - x0(d[2]); })
93        .transition()
94          .duration(duration)
95          .attr("y", function(d) { return x1(d[2]); })
96          .attr("height", function(d) { return x1(d[0]) - x1(d[2]); });
97
98      box.transition()
99          .duration(duration)
100          .attr("y", function(d) { return x1(d[2]); })
101          .attr("height", function(d) { return x1(d[0]) - x1(d[2]); });
102
103      // Update median line.
104      var medianLine = g.selectAll("line.median")
105          .data([quartileData[1]]);
106
107      medianLine.enter().append("svg:line")
108          .attr("class", "median")
109          .attr("x1", 0)
110          .attr("y1", x0)
111          .attr("x2", width)
112          .attr("y2", x0)
113        .transition()
114          .duration(duration)
115          .attr("y1", x1)
116          .attr("y2", x1);
117
118      medianLine.transition()
119          .duration(duration)
120          .attr("y1", x1)
121          .attr("y2", x1);
122
123      // Update whiskers.
124      var whisker = g.selectAll("line.whisker")
125          .data(whiskerData || []);
126
127      whisker.enter().insert("svg:line", "circle, text")
128          .attr("class", "whisker")
129          .attr("x1", 0)
130          .attr("y1", x0)
131          .attr("x2", width)
132          .attr("y2", x0)
133          .style("opacity", 1e-6)
134        .transition()
135          .duration(duration)
136          .attr("y1", x1)
137          .attr("y2", x1)
138          .style("opacity", 1);
139
140      whisker.transition()
141          .duration(duration)
142          .attr("y1", x1)
143          .attr("y2", x1)
144          .style("opacity", 1);
145
146      whisker.exit().transition()
147          .duration(duration)
148          .attr("y1", x1)
149          .attr("y2", x1)
150          .style("opacity", 1e-6)
151          .remove();
152
153      // Update outliers.
154      var outlier = g.selectAll("circle.outlier")
155          .data(outlierIndices, Number);
156
157      outlier.enter().insert("svg:circle", "text")
158          .attr("class", "outlier")
159          .attr("r", 5)
160          .attr("cx", width / 2)
161          .attr("cy", function(i) { return x0(d[i]); })
162          .style("opacity", 1e-6)
163        .transition()
164          .duration(duration)
165          .attr("cy", function(i) { return x1(d[i]); })
166          .style("opacity", 1);
167
168      outlier.transition()
169          .duration(duration)
170          .attr("cy", function(i) { return x1(d[i]); })
171          .style("opacity", 1);
172
173      outlier.exit().transition()
174          .duration(duration)
175          .attr("cy", function(i) { return x1(d[i]); })
176          .style("opacity", 1e-6)
177          .remove();
178
179      // Compute the tick format.
180      var format = tickFormat || x1.tickFormat(8);
181
182      // Update box ticks.
183      var boxTick = g.selectAll("text.box")
184          .data(quartileData);
185
186      boxTick.enter().append("svg:text")
187          .attr("class", "box")
188          .attr("dy", ".3em")
189          .attr("dx", function(d, i) { return i & 1 ? 6 : -6 })
190          .attr("x", function(d, i) { return i & 1 ? width : 0 })
191          .attr("y", x0)
192          .attr("text-anchor", function(d, i) { return i & 1 ? "start" : "end"; })
193          .text(format)
194        .transition()
195          .duration(duration)
196          .attr("y", x1);
197
198      boxTick.transition()
199          .duration(duration)
200          .text(format)
201          .attr("y", x1);
202
203      // Update whisker ticks. These are handled separately from the box
204      // ticks because they may or may not exist, and we want don't want
205      // to join box ticks pre-transition with whisker ticks post-.
206      var whiskerTick = g.selectAll("text.whisker")
207          .data(whiskerData || []);
208
209      whiskerTick.enter().append("svg:text")
210          .attr("class", "whisker")
211          .attr("dy", ".3em")
212          .attr("dx", 6)
213          .attr("x", width)
214          .attr("y", x0)
215          .text(format)
216          .style("opacity", 1e-6)
217        .transition()
218          .duration(duration)
219          .attr("y", x1)
220          .style("opacity", 1);
221
222      whiskerTick.transition()
223          .duration(duration)
224          .text(format)
225          .attr("y", x1)
226          .style("opacity", 1);
227
228      whiskerTick.exit().transition()
229          .duration(duration)
230          .attr("y", x1)
231          .style("opacity", 1e-6)
232          .remove();
233    });
234    d3.timer.flush();
235  }
236
237  box.width = function(x) {
238    if (!arguments.length) return width;
239    width = x;
240    return box;
241  };
242
243  box.height = function(x) {
244    if (!arguments.length) return height;
245    height = x;
246    return box;
247  };
248
249  box.tickFormat = function(x) {
250    if (!arguments.length) return tickFormat;
251    tickFormat = x;
252    return box;
253  };
254
255  box.duration = function(x) {
256    if (!arguments.length) return duration;
257    duration = x;
258    return box;
259  };
260
261  box.domain = function(x) {
262    if (!arguments.length) return domain;
263    domain = x == null ? x : d3.functor(x);
264    return box;
265  };
266
267  box.value = function(x) {
268    if (!arguments.length) return value;
269    value = x;
270    return box;
271  };
272
273  box.whiskers = function(x) {
274    if (!arguments.length) return whiskers;
275    whiskers = x;
276    return box;
277  };
278
279  box.quartiles = function(x) {
280    if (!arguments.length) return quartiles;
281    quartiles = x;
282    return box;
283  };
284
285  return box;
286};
287
288function d3_chart_boxWhiskers(d) {
289  return [0, d.length - 1];
290}
291
292function d3_chart_boxQuartiles(d) {
293  return [
294    d3.quantile(d, .25),
295    d3.quantile(d, .5),
296    d3.quantile(d, .75)
297  ];
298}
299// Chart design based on the recommendations of Stephen Few. Implementation
300// based on the work of Clint Ivy, Jamie Love, and Jason Davies.
301// http://projects.instantcognition.com/protovis/bulletchart/
302d3.chart.bullet = function() {
303  var orient = "left", // TODO top & bottom
304      reverse = false,
305      duration = 0,
306      ranges = d3_chart_bulletRanges,
307      markers = d3_chart_bulletMarkers,
308      measures = d3_chart_bulletMeasures,
309      width = 380,
310      height = 30,
311      tickFormat = null;
312
313  // For each small multiple

314  function bullet(g) {
315    g.each(function(d, i) {
316      var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
317          markerz = markers.call(this, d, i).slice().sort(d3.descending),
318          measurez = measures.call(this, d, i).slice().sort(d3.descending),
319          g = d3.select(this);
320
321      // Compute the new x-scale.
322      var x1 = d3.scale.linear()
323          .domain([0, Math.max(rangez[0], markerz[0], measurez[0])])
324          .range(reverse ? [width, 0] : [0, width]);
325
326      // Retrieve the old x-scale, if this is an update.
327      var x0 = this.__chart__ || d3.scale.linear()
328          .domain([0, Infinity])
329          .range(x1.range());
330
331      // Stash the new scale.
332      this.__chart__ = x1;
333
334      // Derive width-scales from the x-scales.
335      var w0 = d3_chart_bulletWidth(x0),
336          w1 = d3_chart_bulletWidth(x1);
337
338      // Update the range rects.
339      var range = g.selectAll("rect.range")
340          .data(rangez);
341
342      range.enter().append("svg:rect")
343          .attr("class", function(d, i) { return "range s" + i; })
344          .attr("width", w0)
345          .attr("height", height)
346          .attr("x", reverse ? x0 : 0)
347        .transition()
348          .duration(duration)
349          .attr("width", w1)
350          .attr("x", reverse ? x1 : 0);
351
352      range.transition()
353          .duration(duration)
354          .attr("x", reverse ? x1 : 0)
355          .attr("width", w1)
356          .attr("height", height);
357
358      // Update the measure rects.
359      var measure = g.selectAll("rect.measure")
360          .data(measurez);
361
362      measure.enter().append("svg:rect")
363          .attr("class", function(d, i) { return "measure s" + i; })
364          .attr("width", w0)
365          .attr("height", height / 3)
366          .attr("x", reverse ? x0 : 0)
367          .attr("y", height / 3)
368        .transition()
369          .duration(duration)
370          .attr("width", w1)
371          .attr("x", reverse ? x1 : 0);
372
373      measure.transition()
374          .duration(duration)
375          .attr("width", w1)
376          .attr("height", height / 3)
377          .attr("x", reverse ? x1 : 0)
378          .attr("y", height / 3);
379
380      // Update the marker lines.
381      var marker = g.selectAll("line.marker")
382          .data(markerz);
383
384      marker.enter().append("svg:line")
385          .attr("class", "marker")
386          .attr("x1", x0)
387          .attr("x2", x0)
388          .attr("y1", height / 6)
389          .attr("y2", height * 5 / 6)
390        .transition()
391          .duration(duration)
392          .attr("x1", x1)
393          .attr("x2", x1);
394
395      marker.transition()
396          .duration(duration)
397          .attr("x1", x1)
398          .attr("x2", x1)
399          .attr("y1", height / 6)
400          .attr("y2", height * 5 / 6);
401
402      // Compute the tick format.
403      var format = tickFormat || x1.tickFormat(8);
404
405      // Update the tick groups.
406      var tick = g.selectAll("g.tick")
407          .data(x1.ticks(8), function(d) {
408            return this.textContent || format(d);
409          });
410
411      // Initialize the ticks with the old scale, x0.
412      var tickEnter = tick.enter().append("svg:g")
413          .attr("class", "tick")
414          .attr("transform", d3_chart_bulletTranslate(x0))
415          .style("opacity", 1e-6);
416
417      tickEnter.append("svg:line")
418          .attr("y1", height)
419          .attr("y2", height * 7 / 6);
420
421      tickEnter.append("svg:text")
422          .attr("text-anchor", "middle")
423          .attr("dy", "1em")
424          .attr("y", height * 7 / 6)
425          .text(format);
426
427      // Transition the entering ticks to the new scale, x1.
428      tickEnter.transition()
429          .duration(duration)
430          .attr("transform", d3_chart_bulletTranslate(x1))
431          .style("opacity", 1);
432
433      // Transition the updating ticks to the new scale, x1.
434      var tickUpdate = tick.transition()
435          .duration(duration)
436          .attr("transform", d3_chart_bulletTranslate(x1))
437          .style("opacity", 1);
438
439      tickUpdate.select("line")
440          .attr("y1", height)
441          .attr("y2", height * 7 / 6);
442
443      tickUpdate.select("text")
444          .attr("y", height * 7 / 6);
445
446      // Transition the exiting ticks to the new scale, x1.
447      tick.exit().transition()
448          .duration(duration)
449          .attr("transform", d3_chart_bulletTranslate(x1))
450          .style("opacity", 1e-6)
451          .remove();
452    });
453    d3.timer.flush();
454  }
455
456  // left, right, top, bottom
457  bullet.orient = function(x) {
458    if (!arguments.length) return orient;
459    orient = x;
460    reverse = orient == "right" || orient == "bottom";
461    return bullet;
462  };
463
464  // ranges (bad, satisfactory, good)
465  bullet.ranges = function(x) {
466    if (!arguments.length) return ranges;
467    ranges = x;
468    return bullet;
469  };
470
471  // markers (previous, goal)
472  bullet.markers = function(x) {
473    if (!arguments.length) return markers;
474    markers = x;
475    return bullet;
476  };
477
478  // measures (actual, forecast)
479  bullet.measures = function(x) {
480    if (!arguments.length) return measures;
481    measures = x;
482    return bullet;
483  };
484
485  bullet.width = function(x) {
486    if (!arguments.length) return width;
487    width = x;
488    return bullet;
489  };
490
491  bullet.height = function(x) {
492    if (!arguments.length) return height;
493    height = x;
494    return bullet;
495  };
496
497  bullet.tickFormat = function(x) {
498    if (!arguments.length) return tickFormat;
499    tickFormat = x;
500    return bullet;
501  };
502
503  bullet.duration = function(x) {
504    if (!arguments.length) return duration;
505    duration = x;
506    return bullet;
507  };
508
509  return bullet;
510};
511
512function d3_chart_bulletRanges(d) {
513  return d.ranges;
514}
515
516function d3_chart_bulletMarkers(d) {
517  return d.markers;
518}
519
520function d3_chart_bulletMeasures(d) {
521  return d.measures;
522}
523
524function d3_chart_bulletTranslate(x) {
525  return function(d) {
526    return "translate(" + x(d) + ",0)";
527  };
528}
529
530function d3_chart_bulletWidth(x) {
531  var x0 = x(0);
532  return function(d) {
533    return Math.abs(x(d) - x0);
534  };
535}
536// Implements a horizon layout, which is a variation of a single-series
537// area chart where the area is folded into multiple bands. Color is used to
538// encode band, allowing the size of the chart to be reduced significantly
539// without impeding readability. This layout algorithm is based on the work of
540// J. Heer, N. Kong and M. Agrawala in "Sizing the Horizon: The Effects of Chart
541// Size and Layering on the Graphical Perception of Time Series Visualizations",
542// CHI 2009. http://hci.stanford.edu/publications/2009/heer-horizon-chi09.pdf
543d3.chart.horizon = function() {
544  var bands = 1, // between 1 and 5, typically
545      mode = "offset", // or mirror
546      interpolate = "linear", // or basis, monotone, step-before, etc.
547      x = d3_chart_horizonX,
548      y = d3_chart_horizonY,
549      w = 960,
550      h = 40,
551      duration = 0;
552
553  var color = d3.scale.linear()
554      .domain([-1, 0, 1])
555      .range(["#d62728", "#fff", "#1f77b4"]);
556
557  // For each small multiple

558  function horizon(g) {
559    g.each(function(d, i) {
560      var g = d3.select(this),
561          n = 2 * bands + 1,
562          xMin = Infinity,
563          xMax = -Infinity,
564          yMax = -Infinity,
565          x0, // old x-scale
566          y0, // old y-scale
567          id; // unique id for paths
568
569      // Compute x- and y-values along with extents.
570      var data = d.map(function(d, i) {
571        var xv = x.call(this, d, i),
572            yv = y.call(this, d, i);
573        if (xv < xMin) xMin = xv;
574        if (xv > xMax) xMax = xv;
575        if (-yv > yMax) yMax = -yv;
576        if (yv > yMax) yMax = yv;
577        return [xv, yv];
578      });
579
580      // Compute the new x- and y-scales.
581      var x1 = d3.scale.linear().domain([xMin, xMax]).range([0, w]),
582          y1 = d3.scale.linear().domain([0, yMax]).range([0, h * bands]);
583
584      // Retrieve the old scales, if this is an update.
585      if (this.__chart__) {
586        x0 = this.__chart__.x;
587        y0 = this.__chart__.y;
588        id = this.__chart__.id;
589      } else {
590        x0 = d3.scale.linear().domain([0, Infinity]).range(x1.range());
591        y0 = d3.scale.linear().domain([0, Infinity]).range(y1.range());
592        id = ++d3_chart_horizonId;
593      }
594
595      // We'll use a defs to store the area path and the clip path.
596      var defs = g.selectAll("defs")
597          .data([data]);
598
599      var defsEnter = defs.enter().append("svg:defs");
600
601      // The clip path is a simple rect.
602      defsEnter.append("svg:clipPath")
603          .attr("id", "d3_chart_horizon_clip" + id)
604        .append("svg:rect")
605          .attr("width", w)
606          .attr("height", h);
607
608      defs.select("rect").transition()
609          .duration(duration)
610          .attr("width", w)
611          .attr("height", h);
612
613      // The area path is rendered with our resuable d3.svg.area.
614      defsEnter.append("svg:path")
615          .attr("id", "d3_chart_horizon_path" + id)
616          .attr("d", d3_chart_horizonArea
617          .interpolate(interpolate)
618          .x(function(d) { return x0(d[0]); })
619          .y0(h * bands)
620          .y1(function(d) { return h * bands - y0(d[1]); }))
621        .transition()
622          .duration(duration)
623          .attr("d", d3_chart_horizonArea
624          .x(function(d) { return x1(d[0]); })
625          .y1(function(d) { return h * bands - y1(d[1]); }));
626
627      defs.select("path").transition()
628          .duration(duration)
629          .attr("d", d3_chart_horizonArea);
630
631      // We'll use a container to clip all horizon layers at once.
632      g.selectAll("g")
633          .data([null])
634        .enter().append("svg:g")
635          .attr("clip-path", "url(#d3_chart_horizon_clip" + id + ")");
636
637      // Define the transform function based on the mode.
638      var transform = mode == "offset"
639          ? function(d) { return "translate(0," + (d + (d < 0) - bands) * h + ")"; }
640          : function(d) { return (d < 0 ? "scale(1,-1)" : "") + "translate(0," + (d - bands) * h + ")"; };
641
642      // Instantiate each copy of the path with different transforms.
643      var u = g.select("g").selectAll("use")
644          .data(d3.range(-1, -bands - 1, -1).concat(d3.range(1, bands + 1)), Number);
645
646      // TODO don't fudge the enter transition
647      u.enter().append("svg:use")
648          .attr("xlink:href", "#d3_chart_horizon_path" + id)
649          .attr("transform", function(d) { return transform(d + (d > 0 ? 1 : -1)); })
650          .style("fill", color)
651        .transition()
652          .duration(duration)
653          .attr("transform", transform);
654
655      u.transition()
656          .duration(duration)
657          .attr("transform", transform)
658          .style("fill", color);
659
660      u.exit().transition()
661          .duration(duration)
662          .attr("transform", transform)
663          .remove();
664
665      // Stash the new scales.
666      this.__chart__ = {x: x1, y: y1, id: id};
667    });
668    d3.timer.flush();
669  }
670
671  horizon.duration = function(x) {
672    if (!arguments.length) return duration;
673    duration = +x;
674    return horizon;
675  };
676
677  horizon.bands = function(x) {
678    if (!arguments.length) return bands;
679    bands = +x;
680    color.domain([-bands, 0, bands]);
681    return horizon;
682  };
683
684  horizon.mode = function(x) {
685    if (!arguments.length) return mode;
686    mode = x + "";
687    return horizon;
688  };
689
690  horizon.colors = function(x) {
691    if (!arguments.length) return color.range();
692    color.range(x);
693    return horizon;
694  };
695
696  horizon.interpolate = function(x) {
697    if (!arguments.length) return interpolate;
698    interpolate = x + "";
699    return horizon;
700  };
701
702  horizon.x = function(z) {
703    if (!arguments.length) return x;
704    x = z;
705    return horizon;
706  };
707
708  horizon.y = function(z) {
709    if (!arguments.length) return y;
710    y = z;
711    return horizon;
712  };
713
714  horizon.width = function(x) {
715    if (!arguments.length) return w;
716    w = +x;
717    return horizon;
718  };
719
720  horizon.height = function(x) {
721    if (!arguments.length) return h;
722    h = +x;
723    return horizon;
724  };
725
726  return horizon;
727};
728
729var d3_chart_horizonArea = d3.svg.area(),
730    d3_chart_horizonId = 0;
731
732function d3_chart_horizonX(d) {
733  return d[0];
734}
735
736function d3_chart_horizonY(d) {
737  return d[1];
738}
739// Based on http://vis.stanford.edu/protovis/ex/qqplot.html
740d3.chart.qq = function() {
741  var width = 1,
742      height = 1,
743      duration = 0,
744      domain = null,
745      tickFormat = null,
746      n = 100,
747      x = d3_chart_qqX,
748      y = d3_chart_qqY;
749
750  // For each small multiple

751  function qq(g) {
752    g.each(function(d, i) {
753      var g = d3.select(this),
754          qx = d3_chart_qqQuantiles(n, x.call(this, d, i)),
755          qy = d3_chart_qqQuantiles(n, y.call(this, d, i)),
756          xd = domain && domain.call(this, d, i) || [d3.min(qx), d3.max(qx)], // new x-domain
757          yd = domain && domain.call(this, d, i) || [d3.min(qy), d3.max(qy)], // new y-domain
758          x0, // old x-scale
759          y0; // old y-scale
760
761      // Compute the new x-scale.
762      var x1 = d3.scale.linear()
763          .domain(xd)
764          .range([0, width]);
765
766      // Compute the new y-scale.
767      var y1 = d3.scale.linear()
768          .domain(yd)
769          .range([height, 0]);
770
771      // Retrieve the old scales, if this is an update.
772      if (this.__chart__) {
773        x0 = this.__chart__.x;
774        y0 = this.__chart__.y;
775      } else {
776        x0 = d3.scale.linear().domain([0, Infinity]).range(x1.range());
777        y0 = d3.scale.linear().domain([0, Infinity]).range(y1.range());
778      }
779
780      // Stash the new scales.
781      this.__chart__ = {x: x1, y: y1};
782
783      // Update diagonal line.
784      var diagonal = g.selectAll("line.diagonal")
785          .data([null]);
786
787      diagonal.enter().append("svg:line")
788          .attr("class", "diagonal")
789          .attr("x1", x1(yd[0]))
790          .attr("y1", y1(xd[0]))
791          .attr("x2", x1(yd[1]))
792          .attr("y2", y1(xd[1]));
793
794      diagonal.transition()
795          .duration(duration)
796          .attr("x1", x1(yd[0]))
797          .attr("y1", y1(xd[0]))
798          .attr("x2", x1(yd[1]))
799          .attr("y2", y1(xd[1]));
800
801      // Update quantile plots.
802      var circle = g.selectAll("circle")
803          .data(d3.range(n).map(function(i) {
804            return {x: qx[i], y: qy[i]};
805          }));
806
807      circle.enter().append("svg:circle")
808          .attr("class", "quantile")
809          .attr("r", 4.5)
810          .attr("cx", function(d) { return x0(d.x); })
811          .attr("cy", function(d) { return y0(d.y); })
812          .style("opacity", 1e-6)
813        .transition()
814          .duration(duration)
815          .attr("cx", function(d) { return x1(d.x); })
816          .attr("cy", function(d) { return y1(d.y); })
817          .style("opacity", 1);
818
819      circle.transition()
820          .duration(duration)
821          .attr("cx", function(d) { return x1(d.x); })
822          .attr("cy", function(d) { return y1(d.y); })
823          .style("opacity", 1);
824
825      circle.exit().transition()
826          .duration(duration)
827          .attr("cx", function(d) { return x1(d.x); })
828          .attr("cy", function(d) { return y1(d.y); })
829          .style("opacity", 1e-6)
830          .remove();
831
832      var xformat = tickFormat || x1.tickFormat(4),
833          yformat = tickFormat || y1.tickFormat(4),
834          tx = function(d) { return "translate(" + x1(d) + "," + height + ")"; },
835          ty = function(d) { return "translate(0," + y1(d) + ")"; };
836
837      // Update x-ticks.
838      var xtick = g.selectAll("g.x.tick")
839          .data(x1.ticks(4), function(d) {
840            return this.textContent || xformat(d);
841          });
842
843      var xtickEnter = xtick.enter().append("svg:g")
844          .attr("class", "x tick")
845          .attr("transform", function(d) { return "translate(" + x0(d) + "," + height + ")"; })
846          .style("opacity", 1e-6);
847
848      xtickEnter.append("svg:line")
849          .attr("y1", 0)
850          .attr("y2", -6);
851
852      xtickEnter.append("svg:text")
853          .attr("text-anchor", "middle")
854          .attr("dy", "1em")
855          .text(xformat);
856
857      // Transition the entering ticks to the new scale, x1.
858      xtickEnter.transition()
859          .duration(duration)
860          .attr("transform", tx)
861          .style("opacity", 1);
862
863      // Transition the updating ticks to the new scale, x1.
864      xtick.transition()
865          .duration(duration)
866          .attr("transform", tx)
867          .style("opacity", 1);
868
869      // Transition the exiting ticks to the new scale, x1.
870      xtick.exit().transition()
871          .duration(duration)
872          .attr("transform", tx)
873          .style("opacity", 1e-6)
874          .remove();
875
876      // Update ticks.
877      var ytick = g.selectAll("g.y.tick")
878          .data(y1.ticks(4), function(d) {
879            return this.textContent || yformat(d);
880          });
881
882      var ytickEnter = ytick.enter().append("svg:g")
883          .attr("class", "y tick")
884          .attr("transform", function(d) { return "translate(0," + y0(d) + ")"; })
885          .style("opacity", 1e-6);
886
887      ytickEnter.append("svg:line")
888          .attr("x1", 0)
889          .attr("x2", 6);
890
891      ytickEnter.append("svg:text")
892          .attr("text-anchor", "end")
893          .attr("dx", "-.5em")
894          .attr("dy", ".3em")
895          .text(yformat);
896
897      // Transition the entering ticks to the new scale, y1.
898      ytickEnter.transition()
899          .duration(duration)
900          .attr("transform", ty)
901          .style("opacity", 1);
902
903      // Transition the updating ticks to the new scale, y1.
904      ytick.transition()
905          .duration(duration)
906          .attr("transform", ty)
907          .style("opacity", 1);
908
909      // Transition the exiting ticks to the new scale, y1.
910      ytick.exit().transition()
911          .duration(duration)
912          .attr("transform", ty)
913          .style("opacity", 1e-6)
914          .remove();
915    });
916  }
917
918  qq.width = function(x) {
919    if (!arguments.length) return width;
920    width = x;
921    return qq;
922  };
923
924  qq.height = function(x) {
925    if (!arguments.length) return height;
926    height = x;
927    return qq;
928  };
929
930  qq.duration = function(x) {
931    if (!arguments.length) return duration;
932    duration = x;
933    return qq;
934  };
935
936  qq.domain = function(x) {
937    if (!arguments.length) return domain;
938    domain = x == null ? x : d3.functor(x);
939    return qq;
940  };
941
942  qq.count = function(z) {
943    if (!arguments.length) return n;
944    n = z;
945    return qq;
946  };
947
948  qq.x = function(z) {
949    if (!arguments.length) return x;
950    x = z;
951    return qq;
952  };
953
954  qq.y = function(z) {
955    if (!arguments.length) return y;
956    y = z;
957    return qq;
958  };
959
960  qq.tickFormat = function(x) {
961    if (!arguments.length) return tickFormat;
962    tickFormat = x;
963    return qq;
964  };
965
966  return qq;
967};
968
969function d3_chart_qqQuantiles(n, values) {
970  var m = values.length - 1;
971  values = values.slice().sort(d3.ascending);
972  return d3.range(n).map(function(i) {
973    return values[~~(i * m / n)];
974  });
975}
976
977function d3_chart_qqX(d) {
978  return d.x;
979}
980
981function d3_chart_qqY(d) {
982  return d.y;
983}
984})();
Note: See TracBrowser for help on using the repository browser.