```  1 /**
2  * Constructs a default quantile scale. The arguments to this constructor are
3  * optional, and equivalent to calling {@link #domain}. The default domain is
4  * the empty set, and the default range is [0,1].
5  *
6  * @class Represents a quantile scale; a function that maps from a value within
7  * a sortable domain to a quantized numeric range. Typically, the domain is a
8  * set of numbers, but any sortable value (such as strings) can be used as the
9  * domain of a quantile scale. The range defaults to [0,1], with 0 corresponding
10  * to the smallest value in the domain, 1 the largest, .5 the median, etc.
11  *
12  * <p>By default, the number of quantiles in the range corresponds to the number
13  * of values in the domain. The {@link #quantiles} method can be used to specify
14  * an explicit number of quantiles; for example, <tt>quantiles(4)</tt> produces
15  * a standard quartile scale. A quartile scale's range is a set of four discrete
16  * values, such as [0, 1/3, 2/3, 1]. Calling the {@link #range} method will
17  * scale these discrete values accordingly, similar to {@link
18  * pv.Scale.ordinal#splitFlush}.
19  *
20  * <p>For example, given the strings ["c", "a", "b"], a default quantile scale:
21  *
22  * <pre>pv.Scale.quantile("c", "a", "b")</pre>
23  *
24  * will return 0 for "a", .5 for "b", and 1 for "c".
25  *
26  * @extends pv.Scale
27  */
28 pv.Scale.quantile = function() {
29   var n = -1, // number of quantiles
30       j = -1, // max quantile index
31       q = [], // quantile boundaries
32       d = [], // domain
33       y = pv.Scale.linear(); // range
34
35   /** @private */
36   function scale(x) {
37     return y(Math.max(0, Math.min(j, pv.search.index(q, x) - 1)) / j);
38   }
39
40   /**
41    * Sets or gets the quantile boundaries. By default, each element in the
42    * domain is in its own quantile. If the argument to this method is a number,
43    * it specifies the number of equal-sized quantiles by which to divide the
44    * domain.
45    *
46    * <p>If no arguments are specified, this method returns the quantile
47    * boundaries; the first element is always the minimum value of the domain,
48    * and the last element is the maximum value of the domain. Thus, the length
49    * of the returned array is always one greater than the number of quantiles.
50    *
51    * @function
52    * @name pv.Scale.quantile.prototype.quantiles
53    * @param {number} x the number of quantiles.
54    */
55   scale.quantiles = function(x) {
56     if (arguments.length) {
57       n = Number(x);
58       if (n < 0) {
59         q = [d[0]].concat(d);
60         j = d.length - 1;
61       } else {
62         q = [];
63         q[0] = d[0];
64         for (var i = 1; i <= n; i++) {
65           q[i] = d[~~(i * (d.length - 1) / n)];
66         }
67         j = n - 1;
68       }
69       return this;
70     }
71     return q;
72   };
73
74   /**
75    * Sets or gets the input domain. This method can be invoked several ways:
76    *
77    * <p>1. <tt>domain(values...)</tt>
78    *
79    * <p>Specifying the domain as a series of values is the most explicit and
80    * recommended approach. However, if the domain values are derived from data,
81    * you may find the second method more appropriate.
82    *
83    * <p>2. <tt>domain(array, f)</tt>
84    *
85    * <p>Rather than enumerating the domain values as explicit arguments to this
86    * method, you can specify a single argument of an array. In addition, you can
87    * specify an optional accessor function to extract the domain values from the
88    * array.
89    *
90    * <p>3. <tt>domain()</tt>
91    *
92    * <p>Invoking the <tt>domain</tt> method with no arguments returns the
93    * current domain as an array.
94    *
95    * @function
96    * @name pv.Scale.quantile.prototype.domain
97    * @param {...} domain... domain values.
98    * @returns {pv.Scale.quantile} <tt>this</tt>, or the current domain.
99    */
100   scale.domain = function(array, f) {
101     if (arguments.length) {
102       d = (array instanceof Array)
103           ? pv.map(array, f)
104           : Array.prototype.slice.call(arguments);
105       d.sort(pv.naturalOrder);
106       scale.quantiles(n); // recompute quantiles
107       return this;
108     }
109     return d;
110   };
111
112   /**
113    * Sets or gets the output range. This method can be invoked several ways:
114    *
115    * <p>1. <tt>range(min, ..., max)</tt>
116    *
117    * <p>The range may be specified as a series of numbers or colors. Most
118    * commonly, two numbers are specified: the minimum and maximum pixel values.
119    * For a color scale, values may be specified as {@link pv.Color}s or
120    * equivalent strings. For a diverging scale, or other subdivided non-uniform
121    * scales, multiple values can be specified. For example:
122    *
123    * <pre>    .range("red", "white", "green")</pre>
124    *
125    * <p>Currently, only numbers and colors are supported as range values. The
126    * number of range values must exactly match the number of domain values, or
127    * the behavior of the scale is undefined.
128    *
129    * <p>2. <tt>range()</tt>
130    *
131    * <p>Invoking the <tt>range</tt> method with no arguments returns the current
132    * range as an array of numbers or colors.
133    *
134    * @function
135    * @name pv.Scale.quantile.prototype.range
136    * @param {...} range... range values.
137    * @returns {pv.Scale.quantile} <tt>this</tt>, or the current range.
138    */
139   scale.range = function() {
140     if (arguments.length) {
141       y.range.apply(y, arguments);
142       return this;
143     }
144     return y.range();
145   };
146
147   /**
148    * Returns a view of this scale by the specified accessor function <tt>f</tt>.
149    * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to
150    * <tt>function(d) y(d.foo)</tt>.
151    *
152    * <p>This method is provided for convenience, such that scales can be
153    * succinctly defined inline. For example, given an array of data elements
154    * that have a <tt>score</tt> attribute with the domain [0, 1], the height
155    * property could be specified as:
156    *
157    * <pre>.height(pv.Scale.linear().range(0, 480).by(function(d) d.score))</pre>
158    *
159    * This is equivalent to:
160    *
161    * <pre>.height(function(d) d.score * 480)</pre>
162    *
163    * This method should be used judiciously; it is typically more clear to
164    * invoke the scale directly, passing in the value to be scaled.
165    *
166    * @function
167    * @name pv.Scale.quantile.prototype.by
168    * @param {function} f an accessor function.
169    * @returns {pv.Scale.quantile} a view of this scale by the specified
170    * accessor function.
171    */
172   scale.by = function(f) {
173     function by() { return scale(f.apply(this, arguments)); }
174     for (var method in scale) by[method] = scale[method];
175     return by;
176   };
177
178   scale.domain.apply(scale, arguments);
179   return scale;
180 };
181 ```