1 /**
  2  * Abstract; see the various scale implementations.
  3  *
  4  * @class Represents a scale; a function that performs a transformation from
  5  * data domain to visual range. For quantitative and quantile scales, the domain
  6  * is expressed as numbers; for ordinal scales, the domain is expressed as
  7  * strings (or equivalently objects with unique string representations). The
  8  * "visual range" may correspond to pixel space, colors, font sizes, and the
  9  * like.
 10  *
 11  * <p>Note that scales are functions, and thus can be used as properties
 12  * directly, assuming that the data associated with a mark is a number. While
 13  * this is convenient for single-use scales, frequently it is desirable to
 14  * define scales globally:
 15  *
 16  * <pre>var y = pv.Scale.linear(0, 100).range(0, 640);</pre>
 17  *
 18  * The <tt>y</tt> scale can now be equivalently referenced within a property:
 19  *
 20  * <pre>    .height(function(d) y(d))</pre>
 21  *
 22  * Alternatively, if the data are not simple numbers, the appropriate value can
 23  * be passed to the <tt>y</tt> scale (e.g., <tt>d.foo</tt>). The {@link #by}
 24  * method similarly allows the data to be mapped to a numeric value before
 25  * performing the linear transformation.
 26  *
 27  * @see pv.Scale.quantitative
 28  * @see pv.Scale.quantile
 29  * @see pv.Scale.ordinal
 30  * @extends function
 31  */
 32 pv.Scale = function() {};
 33 
 34 /**
 35  * @private Returns a function that interpolators from the start value to the
 36  * end value, given a parameter <i>t</i> in [0, 1].
 37  *
 38  * @param start the start value.
 39  * @param end the end value.
 40  */
 41 pv.Scale.interpolator = function(start, end) {
 42   if (typeof start == "number") {
 43     return function(t) {
 44       return t * (end - start) + start;
 45     };
 46   }
 47 
 48   /* For now, assume color. */
 49   start = pv.color(start).rgb();
 50   end = pv.color(end).rgb();
 51   return function(t) {
 52     var a = start.a * (1 - t) + end.a * t;
 53     if (a < 1e-5) a = 0; // avoid scientific notation
 54     return (start.a == 0) ? pv.rgb(end.r, end.g, end.b, a)
 55         : ((end.a == 0) ? pv.rgb(start.r, start.g, start.b, a)
 56         : pv.rgb(
 57             Math.round(start.r * (1 - t) + end.r * t),
 58             Math.round(start.g * (1 - t) + end.g * t),
 59             Math.round(start.b * (1 - t) + end.b * t), a));
 60   };
 61 };
 62 
 63 /**
 64  * Returns a view of this scale by the specified accessor function <tt>f</tt>.
 65  * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to
 66  * <tt>function(d) y(d.foo)</tt>.
 67  *
 68  * <p>This method is provided for convenience, such that scales can be
 69  * succinctly defined inline. For example, given an array of data elements that
 70  * have a <tt>score</tt> attribute with the domain [0, 1], the height property
 71  * could be specified as:
 72  *
 73  * <pre>    .height(pv.Scale.linear().range(0, 480).by(function(d) d.score))</pre>
 74  *
 75  * This is equivalent to:
 76  *
 77  * <pre>    .height(function(d) d.score * 480)</pre>
 78  *
 79  * This method should be used judiciously; it is typically more clear to invoke
 80  * the scale directly, passing in the value to be scaled.
 81  *
 82  * @function
 83  * @name pv.Scale.prototype.by
 84  * @param {function} f an accessor function.
 85  * @returns {pv.Scale} a view of this scale by the specified accessor function.
 86  */
 87