diff --git a/src/traces/image/plot.js b/src/traces/image/plot.js index c5a85c3af8f..60bc5a6cc2d 100644 --- a/src/traces/image/plot.js +++ b/src/traces/image/plot.js @@ -10,17 +10,12 @@ var d3 = require('d3'); var Lib = require('../../lib'); +var strTranslate = Lib.strTranslate; var xmlnsNamespaces = require('../../constants/xmlns_namespaces'); var constants = require('./constants'); var unsupportedBrowsers = Lib.isIOS() || Lib.isSafari() || Lib.isIE(); -function compatibleAxis(ax) { - return ax.type === 'linear' && - // y axis must be reversed but x axis mustn't be - ((ax.range[1] > ax.range[0]) === (ax._id.charAt(0) === 'x')); -} - module.exports = function plot(gd, plotinfo, cdimage, imageLayer) { var xa = plotinfo.xaxis; var ya = plotinfo.yaxis; @@ -31,7 +26,7 @@ module.exports = function plot(gd, plotinfo, cdimage, imageLayer) { var plotGroup = d3.select(this); var cd0 = cd[0]; var trace = cd0.trace; - var fastImage = supportsPixelatedImage && !trace._hasZ && trace._hasSource && compatibleAxis(xa) && compatibleAxis(ya); + var fastImage = supportsPixelatedImage && !trace._hasZ && trace._hasSource && xa.type === 'linear' && ya.type === 'linear'; trace._fastImage = fastImage; var z = cd0.z; @@ -144,8 +139,23 @@ module.exports = function plot(gd, plotinfo, cdimage, imageLayer) { // Pixelated image rendering // http://phrogz.net/tmp/canvas_image_zoom.html // https://developer.mozilla.org/en-US/docs/Web/CSS/image-rendering - image3 - .attr('style', 'image-rendering: optimizeSpeed; image-rendering: -moz-crisp-edges; image-rendering: -o-crisp-edges; image-rendering: -webkit-optimize-contrast; image-rendering: optimize-contrast; image-rendering: crisp-edges; image-rendering: pixelated;'); + var style = 'image-rendering: optimizeSpeed; image-rendering: -moz-crisp-edges; image-rendering: -o-crisp-edges; image-rendering: -webkit-optimize-contrast; image-rendering: optimize-contrast; image-rendering: crisp-edges; image-rendering: pixelated;'; + if(fastImage) { + var xRange = Lib.simpleMap(xa.range, xa.r2l); + var yRange = Lib.simpleMap(ya.range, ya.r2l); + + var flipX = xRange[1] < xRange[0]; + var flipY = yRange[1] > yRange[0]; + if(flipX || flipY) { + var tx = left + imageWidth / 2; + var ty = top + imageHeight / 2; + style += 'transform:' + + strTranslate(tx + 'px', ty + 'px') + + 'scale(' + (flipX ? -1 : 1) + ',' + (flipY ? -1 : 1) + ')' + + strTranslate(-tx + 'px', -ty + 'px') + ';'; + } + } + image3.attr('style', style); var p = new Promise(function(resolve) { if(trace._hasZ) { diff --git a/test/image/baselines/image_source_axis_reverse.png b/test/image/baselines/image_source_axis_reverse.png new file mode 100644 index 00000000000..4c7120a508f Binary files /dev/null and b/test/image/baselines/image_source_axis_reverse.png differ diff --git a/test/image/mocks/image_source_axis_reverse.json b/test/image/mocks/image_source_axis_reverse.json new file mode 100644 index 00000000000..edd7b578c5a --- /dev/null +++ b/test/image/mocks/image_source_axis_reverse.json @@ -0,0 +1,60 @@ +{ + "data": [ + { + "colormodel": "rgba", + "type": "image", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAlCAYAAAAA7LqSAAADmklEQVR4nO2XUWhTVxjHfyfdmKMN1oelK9QSLUVY2RWHrEKtLw66KQX30Ifpg0qxz31wb8O0UNjL1I3h09iDAy000D0Z0DJhEdslBXN7SwZBJmmbErKwNuU2UXDes4d4r700bib33KflD+Ge8+We78sv3/fdcy401VRTTTXVVFMIvxwnEhG5c97fP+lbLPARZHz8qBwY+AAA0zTp69MA/4B8cTo+flRq2n4MY42dMMFgEICRkZ+Uxw2odgigafudq2majn3nWLV8ATGMNdf44cPfd9lVyxeQwcF3MIw1gsEgY2OLzM9fYmDgPebnLzlQquVLj1y//pFcWan+R1eujLm+KxYTHDr0o/K4b6l2GI2efPnY3eTUqSMUiwmgBQDD+ANNC6sOCSgurUxmVAKUSpfp6tpHLJYiFksBL4AXaFr45Vy9lGUkkxmVhpEll9ukra1q6+raB+D68d3d76oK6ZKSjGQyozIWS5HLbVIujwCQyz0jHL4LVIHsT3f3r0xNfS3/zV8jUlpaNsT6eoS2tq/Q9TnSaZOZmRAzMyFKpcvo+hz5fEE5jGcQOxt2GeXzBQC2t6fY3p5y3bu+HnFsqmE8gdh9AThlZGtr67kzDoU6CIU6nPnGxioXLnzD0NCXLC7+rQTGE4jd3MePJ9H1OQBu3PjW2SO2tp6TTvfQ2dnhWjc9PSvs++2rVyl7aun6XTY2Vl22dLqHw4erp97Ozg6WlnqIRqfFqzVqIMADiL3x7cyGrb1733bGds8ArvK6f/+HRkPXVMNHhWj0pMzlNkkmw46tvT1NofAUTXtfPnnyrKbvgwf3yImJpPIznufSOns2C8Dt22EKhafMzq6KfL7XKpfZ1cStrUEeP/YasbY8Hd6KxS/kvXvfkc3eQdO+Z3j4kTh27BMrELCoVCpYlvUqkBBIKWltrb5cLSz8ojQrnpzZEOHwaYaHHwkAISSVSgWAlpYWDCMZMIxkwLIshBAIoXxTBzyA3Lr1p8xm7zA4+DnnzoUEwNWrnzkpEEKQSi04/peXFwMA5XKZoSGT8+c/tHZ7bVwNgcTjJQfixIl2AXDt2qeWlILe3iKAC8LW0lIicOZMdXzggNrDY90g8XhJPnjwswsCqiVlmn8hhETXf3ut38nJhC9vpXU5fR2ErWy2ws2by//pc2WlXE/YN9Ibg9gQ4fDpmhCgvlx8UTxekvF4qeYjJxLptyYmPq6reS9e7Kt7zf9C/wDLc4opp/2WUgAAAABJRU5ErkJggg==" + }, + { + "colormodel": "rgba", + "type": "image", + "xaxis": "x2", + "yaxis": "y2", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAlCAYAAAAA7LqSAAADmklEQVR4nO2XUWhTVxjHfyfdmKMN1oelK9QSLUVY2RWHrEKtLw66KQX30Ifpg0qxz31wb8O0UNjL1I3h09iDAy000D0Z0DJhEdslBXN7SwZBJmmbErKwNuU2UXDes4d4r700bib33KflD+Ge8+We78sv3/fdcy401VRTTTXVVFMIvxwnEhG5c97fP+lbLPARZHz8qBwY+AAA0zTp69MA/4B8cTo+flRq2n4MY42dMMFgEICRkZ+Uxw2odgigafudq2majn3nWLV8ATGMNdf44cPfd9lVyxeQwcF3MIw1gsEgY2OLzM9fYmDgPebnLzlQquVLj1y//pFcWan+R1eujLm+KxYTHDr0o/K4b6l2GI2efPnY3eTUqSMUiwmgBQDD+ANNC6sOCSgurUxmVAKUSpfp6tpHLJYiFksBL4AXaFr45Vy9lGUkkxmVhpEll9ukra1q6+raB+D68d3d76oK6ZKSjGQyozIWS5HLbVIujwCQyz0jHL4LVIHsT3f3r0xNfS3/zV8jUlpaNsT6eoS2tq/Q9TnSaZOZmRAzMyFKpcvo+hz5fEE5jGcQOxt2GeXzBQC2t6fY3p5y3bu+HnFsqmE8gdh9AThlZGtr67kzDoU6CIU6nPnGxioXLnzD0NCXLC7+rQTGE4jd3MePJ9H1OQBu3PjW2SO2tp6TTvfQ2dnhWjc9PSvs++2rVyl7aun6XTY2Vl22dLqHw4erp97Ozg6WlnqIRqfFqzVqIMADiL3x7cyGrb1733bGds8ArvK6f/+HRkPXVMNHhWj0pMzlNkkmw46tvT1NofAUTXtfPnnyrKbvgwf3yImJpPIznufSOns2C8Dt22EKhafMzq6KfL7XKpfZ1cStrUEeP/YasbY8Hd6KxS/kvXvfkc3eQdO+Z3j4kTh27BMrELCoVCpYlvUqkBBIKWltrb5cLSz8ojQrnpzZEOHwaYaHHwkAISSVSgWAlpYWDCMZMIxkwLIshBAIoXxTBzyA3Lr1p8xm7zA4+DnnzoUEwNWrnzkpEEKQSi04/peXFwMA5XKZoSGT8+c/tHZ7bVwNgcTjJQfixIl2AXDt2qeWlILe3iKAC8LW0lIicOZMdXzggNrDY90g8XhJPnjwswsCqiVlmn8hhETXf3ut38nJhC9vpXU5fR2ErWy2ws2by//pc2WlXE/YN9Ibg9gQ4fDpmhCgvlx8UTxekvF4qeYjJxLptyYmPq6reS9e7Kt7zf9C/wDLc4opp/2WUgAAAABJRU5ErkJggg==" + }, + { + "colormodel": "rgba", + "type": "image", + "xaxis": "x3", + "yaxis": "y3", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAlCAYAAAAA7LqSAAADmklEQVR4nO2XUWhTVxjHfyfdmKMN1oelK9QSLUVY2RWHrEKtLw66KQX30Ifpg0qxz31wb8O0UNjL1I3h09iDAy000D0Z0DJhEdslBXN7SwZBJmmbErKwNuU2UXDes4d4r700bib33KflD+Ge8+We78sv3/fdcy401VRTTTXVVFMIvxwnEhG5c97fP+lbLPARZHz8qBwY+AAA0zTp69MA/4B8cTo+flRq2n4MY42dMMFgEICRkZ+Uxw2odgigafudq2majn3nWLV8ATGMNdf44cPfd9lVyxeQwcF3MIw1gsEgY2OLzM9fYmDgPebnLzlQquVLj1y//pFcWan+R1eujLm+KxYTHDr0o/K4b6l2GI2efPnY3eTUqSMUiwmgBQDD+ANNC6sOCSgurUxmVAKUSpfp6tpHLJYiFksBL4AXaFr45Vy9lGUkkxmVhpEll9ukra1q6+raB+D68d3d76oK6ZKSjGQyozIWS5HLbVIujwCQyz0jHL4LVIHsT3f3r0xNfS3/zV8jUlpaNsT6eoS2tq/Q9TnSaZOZmRAzMyFKpcvo+hz5fEE5jGcQOxt2GeXzBQC2t6fY3p5y3bu+HnFsqmE8gdh9AThlZGtr67kzDoU6CIU6nPnGxioXLnzD0NCXLC7+rQTGE4jd3MePJ9H1OQBu3PjW2SO2tp6TTvfQ2dnhWjc9PSvs++2rVyl7aun6XTY2Vl22dLqHw4erp97Ozg6WlnqIRqfFqzVqIMADiL3x7cyGrb1733bGds8ArvK6f/+HRkPXVMNHhWj0pMzlNkkmw46tvT1NofAUTXtfPnnyrKbvgwf3yImJpPIznufSOns2C8Dt22EKhafMzq6KfL7XKpfZ1cStrUEeP/YasbY8Hd6KxS/kvXvfkc3eQdO+Z3j4kTh27BMrELCoVCpYlvUqkBBIKWltrb5cLSz8ojQrnpzZEOHwaYaHHwkAISSVSgWAlpYWDCMZMIxkwLIshBAIoXxTBzyA3Lr1p8xm7zA4+DnnzoUEwNWrnzkpEEKQSi04/peXFwMA5XKZoSGT8+c/tHZ7bVwNgcTjJQfixIl2AXDt2qeWlILe3iKAC8LW0lIicOZMdXzggNrDY90g8XhJPnjwswsCqiVlmn8hhETXf3ut38nJhC9vpXU5fR2ErWy2ws2by//pc2WlXE/YN9Ibg9gQ4fDpmhCgvlx8UTxekvF4qeYjJxLptyYmPq6reS9e7Kt7zf9C/wDLc4opp/2WUgAAAABJRU5ErkJggg==" + }, + { + "colormodel": "rgba", + "type": "image", + "xaxis": "x4", + "yaxis": "y4", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAlCAYAAAAA7LqSAAADmklEQVR4nO2XUWhTVxjHfyfdmKMN1oelK9QSLUVY2RWHrEKtLw66KQX30Ifpg0qxz31wb8O0UNjL1I3h09iDAy000D0Z0DJhEdslBXN7SwZBJmmbErKwNuU2UXDes4d4r700bib33KflD+Ge8+We78sv3/fdcy401VRTTTXVVFMIvxwnEhG5c97fP+lbLPARZHz8qBwY+AAA0zTp69MA/4B8cTo+flRq2n4MY42dMMFgEICRkZ+Uxw2odgigafudq2majn3nWLV8ATGMNdf44cPfd9lVyxeQwcF3MIw1gsEgY2OLzM9fYmDgPebnLzlQquVLj1y//pFcWan+R1eujLm+KxYTHDr0o/K4b6l2GI2efPnY3eTUqSMUiwmgBQDD+ANNC6sOCSgurUxmVAKUSpfp6tpHLJYiFksBL4AXaFr45Vy9lGUkkxmVhpEll9ukra1q6+raB+D68d3d76oK6ZKSjGQyozIWS5HLbVIujwCQyz0jHL4LVIHsT3f3r0xNfS3/zV8jUlpaNsT6eoS2tq/Q9TnSaZOZmRAzMyFKpcvo+hz5fEE5jGcQOxt2GeXzBQC2t6fY3p5y3bu+HnFsqmE8gdh9AThlZGtr67kzDoU6CIU6nPnGxioXLnzD0NCXLC7+rQTGE4jd3MePJ9H1OQBu3PjW2SO2tp6TTvfQ2dnhWjc9PSvs++2rVyl7aun6XTY2Vl22dLqHw4erp97Ozg6WlnqIRqfFqzVqIMADiL3x7cyGrb1733bGds8ArvK6f/+HRkPXVMNHhWj0pMzlNkkmw46tvT1NofAUTXtfPnnyrKbvgwf3yImJpPIznufSOns2C8Dt22EKhafMzq6KfL7XKpfZ1cStrUEeP/YasbY8Hd6KxS/kvXvfkc3eQdO+Z3j4kTh27BMrELCoVCpYlvUqkBBIKWltrb5cLSz8ojQrnpzZEOHwaYaHHwkAISSVSgWAlpYWDCMZMIxkwLIshBAIoXxTBzyA3Lr1p8xm7zA4+DnnzoUEwNWrnzkpEEKQSi04/peXFwMA5XKZoSGT8+c/tHZ7bVwNgcTjJQfixIl2AXDt2qeWlILe3iKAC8LW0lIicOZMdXzggNrDY90g8XhJPnjwswsCqiVlmn8hhETXf3ut38nJhC9vpXU5fR2ErWy2ws2by//pc2WlXE/YN9Ibg9gQ4fDpmhCgvlx8UTxekvF4qeYjJxLptyYmPq6reS9e7Kt7zf9C/wDLc4opp/2WUgAAAABJRU5ErkJggg==" + } + ], + "layout": { + "grid": { + "rows": 2, + "columns": 2, + "pattern": "independent" + }, + "width": 600, + "height": 600, + "margin": { + "t": 35, + "l": 35, + "b": 35, + "r": 35 + }, + "xaxis2": { + "autorange": "reversed" + }, + "yaxis3": { + "range": [ + "8", + "32" + ] + }, + "yaxis4": { + "autorange": true + }, + "xaxis4": { + "autorange": "reversed" + } + } +} diff --git a/test/jasmine/tests/image_test.js b/test/jasmine/tests/image_test.js index 879f3eb47d7..903cde4ee59 100644 --- a/test/jasmine/tests/image_test.js +++ b/test/jasmine/tests/image_test.js @@ -423,9 +423,7 @@ describe('image plot', function() { [ ['yaxis.type', 'log'], - ['xaxis.type', 'log'], - ['xaxis.range', [50, 0]], - ['yaxis.range', [0, 50]] + ['xaxis.type', 'log'] ].forEach(function(attr) { it('does not renders pixelated image when the axes are not compatible', function(done) { var mock = require('@mocks/image_astronaut_source.json'); @@ -669,5 +667,38 @@ describe('image hover:', function() { .then(done); }); }); + + [ + [true, true], + [true, 'reversed'], // the default image layout + ['reversed', true], + ['reversed', 'reversed'] + ].forEach(function(test) { + it('should show correct hover info regardless of axis directions ' + test, function(done) { + var mockCopy = Lib.extendDeep({}, mock); + mockCopy.layout.xaxis.autorange = test[0]; + mockCopy.layout.yaxis.autorange = test[1]; + mockCopy.data[0].colormodel = 'rgba'; + mockCopy.data[0].hovertemplate = 'x:%{x}, y:%{y}, z:%{z}'; + Plotly.newPlot(gd, mockCopy) + .then(function() { + var x = 205; + var y = 125; + + // adjust considering css + if(test[0] === 'reversed') x = 512 - x; + if(test[1] !== 'reversed') y = 512 - y; + _hover(x, y); + }) + .then(function() { + assertHoverLabelContent({ + nums: 'x:205, y:125, z:[202, 148, 125, 255]', + name: '' + }, 'positions should be correct!'); + }) + .catch(failTest) + .then(done); + }); + }); }); }); diff --git a/test/jasmine/tests/mock_test.js b/test/jasmine/tests/mock_test.js index 7cd61e5b034..8aaa8b97456 100644 --- a/test/jasmine/tests/mock_test.js +++ b/test/jasmine/tests/mock_test.js @@ -648,6 +648,7 @@ var list = [ 'image_colormodel', 'image_non_numeric', 'image_opacity', + 'image_source_axis_reverse', 'image_with_gaps', 'image_with_heatmap', 'image_zmin_zmax', @@ -1721,6 +1722,7 @@ figs['image_cat'] = require('@mocks/image_cat'); figs['image_colormodel'] = require('@mocks/image_colormodel'); figs['image_non_numeric'] = require('@mocks/image_non_numeric'); figs['image_opacity'] = require('@mocks/image_opacity'); +figs['image_source_axis_reverse'] = require('@mocks/image_source_axis_reverse'); figs['image_with_gaps'] = require('@mocks/image_with_gaps'); figs['image_with_heatmap'] = require('@mocks/image_with_heatmap'); figs['image_zmin_zmax'] = require('@mocks/image_zmin_zmax');