Parse rgba colors (fixes #517) fix-517
authorDan Vanderkam <danvdk@gmail.com>
Tue, 21 Apr 2015 18:41:39 +0000 (14:41 -0400)
committerDan Vanderkam <danvdk@gmail.com>
Tue, 21 Apr 2015 18:41:39 +0000 (14:41 -0400)
auto_tests/tests/error_bars.js
auto_tests/tests/utils_test.js
src/dygraph-utils.js

index 2fd5ae4..e04acde 100644 (file)
@@ -144,6 +144,53 @@ it('testErrorBarsCorrectColors', function() {
   assert.deepEqual([0, 255, 0, 38], Util.samplePixel(g.hidden_, 200, 225));
 });
 
+// Regression test for https://github.com/danvk/dygraphs/issues/517
+// This verifies that the error bars have alpha=fillAlpha, even if the series
+// color has its own alpha value.
+it('testErrorBarsForAlphaSeriesCorrectColors', function() {
+  var data = [
+    [0, [100, 50]],
+    [2, [100, 50]]
+  ];
+
+  var opts = {
+    errorBars: true,
+    sigma: 1.0,
+    fillAlpha: 0.15,
+    strokeWidth: 10,
+    colors: ['rgba(0, 255, 0, 0.5)'],
+    axes : {
+      x : {
+        drawGrid: false,
+        drawAxis: false,
+      },
+      y : {
+        drawGrid: false,
+        drawAxis: false,
+      }
+    },
+    width: 400,
+    height: 300,
+    valueRange: [0, 300],
+    labels: ['X', 'Y']
+  };
+  var graph = document.getElementById("graph");
+  var g = new Dygraph(graph, data, opts);
+
+  // y-pixels (0=top, 299=bottom)
+  //   0-148: empty (white)
+  // 149-198: Y error bar
+  // 199:     Y center line
+  // 200-248: Y error bar
+  // 249-299: empty (white)
+
+  //  38 = 255 * 0.15 (fillAlpha)
+  // 146 = 255 * (0.15 * 0.5 + 1 * 0.5) (fillAlpha from error bar + alpha from series line)
+  assert.deepEqual([0, 255, 0, 38],  Util.samplePixel(g.hidden_, 1, 175));
+  assert.deepEqual([0, 255, 0, 146], Util.samplePixel(g.hidden_, 200, 199));
+  assert.deepEqual([0, 255, 0, 38],  Util.samplePixel(g.hidden_, 1, 225));
+});
+
 
 // Regression test for http://code.google.com/p/dygraphs/issues/detail?id=392
 it('testRollingAveragePreservesNaNs', function() {
index 765ed1e..e8ebe5a 100644 (file)
@@ -175,6 +175,8 @@ it('testToRGB', function() {
   assert.deepEqual({r: 255, g: 200, b: 150}, Dygraph.toRGB_('rgb(255,200,150)'));
   assert.deepEqual({r: 255, g: 200, b: 150}, Dygraph.toRGB_('#FFC896'));
   assert.deepEqual({r: 255, g: 0, b: 0}, Dygraph.toRGB_('red'));
+  assert.deepEqual({r: 255, g: 200, b: 150, a: 0.6},
+                   Dygraph.toRGB_('rgba(255, 200, 150, 0.6)'));
 });
 
 it('testIsPixelChangingOptionList', function() {
index d222ec3..a960ba7 100644 (file)
@@ -1068,27 +1068,48 @@ Dygraph.pow = function(base, exp) {
   return Math.pow(base, exp);
 };
 
+var RGBA_RE = /^rgba?\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})(?:,\s*([01](?:\.\d+)?))?\)$/;
+
+/**
+ * Helper for Dygraph.toRGB_ which parses strings of the form:
+ * rgb(123, 45, 67)
+ * rgba(123, 45, 67, 0.5)
+ * @return parsed {r,g,b,a?} tuple or null.
+ */
+function parseRGBA(rgbStr) {
+  var bits = RGBA_RE.exec(rgbStr);
+  if (!bits) return null;
+  var r = parseInt(bits[1], 10),
+      g = parseInt(bits[2], 10),
+      b = parseInt(bits[3], 10);
+  if (bits[4]) {
+    return {r: r, g: g, b: b, a: parseFloat(bits[4])};
+  } else {
+    return {r: r, g: g, b: b};
+  }
+}
+
 /**
  * Converts any valid CSS color (hex, rgb(), named color) to an RGB tuple.
  *
  * @param {!string} colorStr Any valid CSS color string.
- * @return {{r:number,g:number,b:number}} Parsed RGB tuple.
+ * @return {{r:number,g:number,b:number,a:number?}} Parsed RGB tuple.
  * @private
  */
 Dygraph.toRGB_ = function(colorStr) {
-  // TODO(danvk): cache color parses to avoid repeated DOM manipulation.
+  // Strategy: First try to parse colorStr directly. This is fast & avoids DOM
+  // manipulation.  If that fails (e.g. for named colors like 'red'), then
+  // create a hidden DOM element and parse its computed color.
+  var rgb = parseRGBA(colorStr);
+  if (rgb) return rgb;
+
   var div = document.createElement('div');
   div.style.backgroundColor = colorStr;
   div.style.visibility = 'hidden';
   document.body.appendChild(div);
   var rgbStr = window.getComputedStyle(div, null).backgroundColor;
   document.body.removeChild(div);
-  var bits = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/.exec(rgbStr);
-  return {
-    r: parseInt(bits[1], 10),
-    g: parseInt(bits[2], 10),
-    b: parseInt(bits[3], 10)
-  };
+  return parseRGBA(rgbStr);
 };
 
 /**