Dygraph.DEFAULT_HEIGHT = 320;
Dygraph.AXIS_LINE_WIDTH = 0.3;
+Dygraph.LOG_SCALE = 10;
+Dygraph.LN_TEN = Math.log(Dygraph.LOG_SCALE);
+Dygraph.log10 = function(x) {
+ return Math.log(x) / Dygraph.LN_TEN;
+}
// Default attribute values.
Dygraph.DEFAULT_ATTRS = {
delimiter: ',',
- logScale: false,
sigma: 2.0,
errorBars: false,
fractions: false,
* axis. Uses the first axis by default.
* Returns a two-element array: [X, Y]
*
- * Note: use toDomXCoord instead of toDomCoords(x. null) and use toDomYCoord
+ * Note: use toDomXCoord instead of toDomCoords(x, null) and use toDomYCoord
* instead of toDomCoords(null, y, axis).
*/
Dygraph.prototype.toDomCoords = function(x, y, axis) {
/**
* Convert from data x coordinates to canvas/div X coordinate.
* If specified, do this conversion for the coordinate system of a particular
- * axis. Uses the first axis by default.
- * returns a single value or null if x is null.
+ * axis.
+ * Returns a single value or null if x is null.
*/
Dygraph.prototype.toDomXCoord = function(x) {
if (x == null) {
* returns a single value or null if y is null.
*/
Dygraph.prototype.toDomYCoord = function(y, axis) {
- var pct = toPercentYCoord(y, axis);
+ var pct = this.toPercentYCoord(y, axis);
if (pct == null) {
return null;
}
+ var area = this.plotter_.area;
return area.y + pct * area.h;
}
* axis. Uses the first axis by default.
* Returns a two-element array: [X, Y].
*
- * Note: use toDataXCoord instead of toDataCoords(x. null) and use toDataYCoord
+ * Note: use toDataXCoord instead of toDataCoords(x, null) and use toDataYCoord
* instead of toDataCoords(null, y, axis).
*/
Dygraph.prototype.toDataCoords = function(x, y, axis) {
var area = this.plotter_.area;
var yRange = this.yAxisRange(axis);
- if (!this.attr_("logscale")) {
+ if (!axis.logscale) {
return yRange[0] + (area.h - y) / area.h * (yRange[1] - yRange[0]);
} else {
// Computing the inverse of toDomCoord.
// the following steps:
//
// Original calcuation:
- // pct = (logr1 - Math.log(y)) / (logr1 - Math.log(yRange[0]));
+ // pct = (logr1 - Dygraph.log10(y)) / (logr1 - Dygraph.log10(yRange[0]));
//
// Move denominator to both sides:
- // pct * (logr1 - Math.log(yRange[0])) = logr1 - Math.log(y);
+ // pct * (logr1 - Dygraph.log10(yRange[0])) = logr1 - Dygraph.log10(y);
//
// subtract logr1, and take the negative value.
- // logr1 - (pct * (logr1 - Math.log(yRange[0]))) = Math.log(y);
+ // logr1 - (pct * (logr1 - Dygraph.log10(yRange[0]))) = Dygraph.log10(y);
//
// Swap both sides of the equation, and we can compute the log of the
// return value. Which means we just need to use that as the exponent in
// e^exponent.
- // Math.log(y) = logr1 - (pct * (logr1 - Math.log(yRange[0])));
+ // Dygraph.log10(y) = logr1 - (pct * (logr1 - Dygraph.log10(yRange[0])));
- var logr1 = Math.log(yRange[1]);
- var exponent = logr1 - (pct * (logr1 - Math.log(yRange[0])));
- var value = Math.pow(Math.E, exponent);
+ var logr1 = Dygraph.log10(yRange[1]);
+ var exponent = logr1 - (pct * (logr1 - Dygraph.log10(yRange[0])));
+ var value = Math.pow(Dygraph.LOG_SCALE, exponent);
return value;
}
};
if (y == null) {
return null;
}
+ if (typeof(axis) == "undefined") axis = 0;
var area = this.plotter_.area;
var yRange = this.yAxisRange(axis);
var pct;
- if (!this.attr_("logscale")) {
+ if (!this.axes_[axis].logscale) {
// yrange[1] - y is unit distance from the bottom.
// yrange[1] - yrange[0] is the scale of the range.
// (yRange[1] - y) / (yRange[1] - yRange[0]) is the % from the bottom.
pct = (yRange[1] - y) / (yRange[1] - yRange[0]);
} else {
- var logr1 = Math.log(yRange[1]);
- pct = (logr1 - Math.log(y)) / (logr1 - Math.log(yRange[0]));
+ var logr1 = Dygraph.log10(yRange[1]);
+ pct = (logr1 - Dygraph.log10(y)) / (logr1 - Dygraph.log10(yRange[0]));
}
return pct;
}
// y-axis scaling is automatic unless this is a full 2D pan.
if (context.is2DPan) {
// Adjust each axis appropriately.
+ // NOTE(konigsberg): I don't think this computation for y_frac is correct.
+ // I think it doesn't take into account the display of the x axis.
+ // See, when I tested this with console.log(y_frac), and move the mouse
+ // cursor to the botom, the largest y_frac was 0.94, and not 1.0. That
+ // could also explain why panning tends to start with a small jumpy shift.
var y_frac = context.dragEndY / g.height_;
+
for (var i = 0; i < g.axes_.length; i++) {
var axis = g.axes_[i];
var maxValue = axis.draggingValue + y_frac * axis.dragValueRange;
* Add ticks when the x axis has numbers on it (instead of dates)
* TODO(konigsberg): Update comment.
*
- * @param {Number} startDate Start of the date window (millis since epoch)
- * @param {Number} endDate End of the date window (millis since epoch)
+ * @param {Number} minV minimum value
+ * @param {Number} maxV maximum value
* @param self
* @param {function} attribute accessor function.
* @return {Array.<Object>} Array of {label, value} tuples.
ticks.push({v: vals[i]});
}
} else {
- if (self.attr_("logscale")) {
+ if (axis_props && attr("logscale")) {
// As opposed to the other ways for computing ticks, we're just going
// for nearby values. There's no reasonable way to scale the values
// (unless we want to show strings like "log(" + x + ")") in which case
// so compute height / pixelsPerTick and move on.
var pixelsPerTick = attr('pixelsPerYLabel');
+ // NOTE(konigsberg): Dan, should self.height_ be self.plotter_.area.h?
var nTicks = Math.floor(self.height_ / pixelsPerTick);
var vv = minV;
// Construct the set of ticks.
for (var i = 0; i < nTicks; i++) {
ticks.push( {v: vv} );
- vv = vv * Math.E;
+ vv = vv * Dygraph.LOG_SCALE;
}
} else {
// Basic idea:
* number of axes, rolling averages, etc.
*/
Dygraph.prototype.predraw_ = function() {
- // TODO(danvk): move more computations out of drawGraph_ and into here.
+ // TODO(danvk): movabilitye more computations out of drawGraph_ and into here.
this.computeYAxes_();
// Create a new plotter.
var seriesName = this.attr_("labels")[i];
var connectSeparatedPoints = this.attr_('connectSeparatedPoints', i);
+ var logScale = this.attr_('logscale', i);
var series = [];
for (var j = 0; j < data.length; j++) {
- if (data[j][i] != null || !connectSeparatedPoints) {
- var date = data[j][0];
- series.push([date, data[j][i]]);
+ var date = data[j][0];
+ var point = data[j][i];
+ if (logScale) {
+ // On the log scale, points less than zero do not exist.
+ // This will create a gap in the chart. Note that this ignores
+ // connectSeparatedPoints.
+ if (point < 0) {
+ point = null;
+ }
+ series.push([date, point]);
+ } else {
+ if (point != null || !connectSeparatedPoints) {
+ series.push([date, point]);
+ }
}
}
'pixelsPerYLabel',
'yAxisLabelWidth',
'axisLabelFontSize',
- 'axisTickSize'
+ 'axisTickSize',
+ 'logscale'
];
// Copy global axis options over to the first axis.
// Compute extreme values, a span and tick marks for each axis.
for (var i = 0; i < this.axes_.length; i++) {
- var isLogScale = this.attr_("logscale");
var axis = this.axes_[i];
if (axis.valueWindow) {
// This is only set if the user has zoomed on the y-axis. It is never set
var maxAxisY;
var minAxisY;
- if (isLogScale) {
+ if (axis.logscale) {
var maxAxisY = maxY + 0.1 * span;
var minAxisY = minY;
} else {