diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js index 5fa39480d6e..44515bbec4b 100644 --- a/src/components/drawing/index.js +++ b/src/components/drawing/index.js @@ -103,6 +103,16 @@ drawing.setRect = function(s, x, y, w, h) { drawing.translatePoint = function(d, sel, xa, ya) { var x = xa.c2p(d.x); var y = ya.c2p(d.y); + if (isNumeric(d.x1)) { + x = (xa.c2p(d.x1) + x) / 2; + } + if (isNumeric(d.y1)) { + y = (ya.c2p(d.y1) + y) / 2; + } + + // Store middle point for use on hover + d.xc = x; + d.yc = y; if(isNumeric(x) && isNumeric(y) && sel.node()) { // for multiline text this works better @@ -377,9 +387,9 @@ drawing.symbolNumber = function(v) { 0 : Math.floor(Math.max(v, 0)); }; -function makePointPath(symbolNumber, r, t, s) { +function makePointPath(symbolNumber, rx, ry, t, s) { var base = symbolNumber % 100; - return drawing.symbolFuncs[base](r, t, s) + (symbolNumber >= 200 ? DOTPATH : ''); + return drawing.symbolFuncs[base](rx, ry, t, s) + (symbolNumber >= 200 ? DOTPATH : ''); } var stopFormatter = numberFormat('~f'); @@ -754,10 +764,10 @@ drawing.getPatternAttr = function(mp, i, dflt) { return mp; }; -drawing.pointStyle = function(s, trace, gd, pt) { +drawing.pointStyle = function(s, trace, xa, ya, gd, pt) { if(!s.size()) return; - var fns = drawing.makePointStyleFns(trace); + var fns = drawing.makePointStyleFns(trace, xa, ya); s.each(function(d) { drawing.singlePointStyle(d, d3.select(this), trace, fns, gd, pt); @@ -775,21 +785,27 @@ drawing.singlePointStyle = function(d, sel, trace, fns, gd, pt) { (d.mo === undefined ? marker.opacity : d.mo) ); - if(fns.ms2mrc) { - var r; + if(fns.ms2mrx && fns.ms2mry) { + var rx, ry; // handle multi-trace graph edit case if(d.ms === 'various' || marker.size === 'various') { - r = 3; + rx = ry = 3; } else { - r = fns.ms2mrc(d.ms); + rx = fns.ms2mrx(d); + ry = fns.ms2mry(d); } // store the calculated size so hover can use it - d.mrc = r; + d.mrxc = rx; + d.mryc = ry; + + // TODO: Can we remove mrc? + d.mrc = Math.max(rx, ry); + // TODO: Update selected size to handle ranges if(fns.selectedSizeFn) { - r = d.mrc = fns.selectedSizeFn(d); + d.mrc = fns.selectedSizeFn(d); } // turn the symbol into a sanitized number @@ -802,7 +818,7 @@ drawing.singlePointStyle = function(d, sel, trace, fns, gd, pt) { var angle = getMarkerAngle(d, trace); var standoff = getMarkerStandoff(d, trace); - sel.attr('d', makePointPath(x, r, angle, standoff)); + sel.attr('d', makePointPath(x, rx, ry, angle, standoff)); } var perPointGradient = false; @@ -920,7 +936,7 @@ drawing.singlePointStyle = function(d, sel, trace, fns, gd, pt) { } }; -drawing.makePointStyleFns = function(trace) { +drawing.makePointStyleFns = function(trace, xa, ya) { var out = {}; var marker = trace.marker; @@ -930,9 +946,28 @@ drawing.makePointStyleFns = function(trace) { out.lineScale = drawing.tryColorscale(marker, 'line'); if(Registry.traceIs(trace, 'symbols')) { - out.ms2mrc = subTypes.isBubble(trace) ? - makeBubbleSizeFn(trace) : - function() { return (marker.size || 6) / 2; }; + const ms2mrc = subTypes.isBubble(trace) + ? makeBubbleSizeFn(trace) + : function() { return (marker.size || 6) / 2; }; + + out.ms2mrx = function (trace) { + var mr = ms2mrc(trace.ms); + if (trace.x1 == null) { + return mr; + } + var x = xa.c2p(trace.x); + var x1 = xa.c2p(trace.x1); + return Math.max(Math.abs(x1 - x) / 2, mr); + } + out.ms2mry = function(trace) { + var mr = ms2mrc(trace.ms) + if (trace.y1 == null) { + return mr; + } + var y = ya.c2p(trace.y); + var y1 = ya.c2p(trace.y1); + return Math.max(Math.abs(y1 - y) / 2, mr); + } } if(trace.selectedpoints) { @@ -1059,7 +1094,7 @@ drawing.selectedPointStyle = function(s, trace) { var mx = d.mx || marker.symbol || 0; var mrc2 = fns.selectedSizeFn(d); - pt.attr('d', makePointPath(drawing.symbolNumber(mx), mrc2, getMarkerAngle(d, trace), getMarkerStandoff(d, trace))); + pt.attr('d', makePointPath(drawing.symbolNumber(mx), mrc2, mrc2, getMarkerAngle(d, trace), getMarkerStandoff(d, trace))); // save for Drawing.selectedTextStyle d.mrc2 = mrc2; diff --git a/src/components/drawing/symbol_defs.js b/src/components/drawing/symbol_defs.js index 3aab9be891c..59a6914ef71 100644 --- a/src/components/drawing/symbol_defs.js +++ b/src/components/drawing/symbol_defs.js @@ -25,17 +25,18 @@ var sin = Math.sin; module.exports = { circle: { n: 0, - f: function(r, angle, standoff) { + f: function(rx, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; - var rs = round(r, 2); - var circle = 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs + 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z'; + var rxs = round(rx, 2); + var rys = round(ry, 2); + var circle = 'M' + rxs + ',0A' + rxs + ',' + rys + ' 0 1,1 0,-' + rys + 'A' + rxs + ',' + rys + ' 0 0,1 ' + rxs + ',0Z'; return standoff ? align(angle, standoff, circle) : circle; } }, square: { n: 1, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rs = round(r, 2); @@ -44,7 +45,7 @@ module.exports = { }, diamond: { n: 2, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rd = round(r * 1.3, 2); @@ -53,7 +54,7 @@ module.exports = { }, cross: { n: 3, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rc = round(r * 0.4, 2); @@ -65,7 +66,7 @@ module.exports = { }, x: { n: 4, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rx = round(r * 0.8 / sqrt2, 2); @@ -78,51 +79,51 @@ module.exports = { }, 'triangle-up': { n: 5, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rt = round(r * 2 / sqrt3, 2); var r2 = round(r / 2, 2); - var rs = round(r, 2); + var rs = round(rx, 2); return align(angle, standoff, 'M-' + rt + ',' + r2 + 'H' + rt + 'L0,-' + rs + 'Z'); } }, 'triangle-down': { n: 6, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rt = round(r * 2 / sqrt3, 2); var r2 = round(r / 2, 2); - var rs = round(r, 2); + var rs = round(rx, 2); return align(angle, standoff, 'M-' + rt + ',-' + r2 + 'H' + rt + 'L0,' + rs + 'Z'); } }, 'triangle-left': { n: 7, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rt = round(r * 2 / sqrt3, 2); var r2 = round(r / 2, 2); - var rs = round(r, 2); + var rs = round(rx, 2); return align(angle, standoff, 'M' + r2 + ',-' + rt + 'V' + rt + 'L-' + rs + ',0Z'); } }, 'triangle-right': { n: 8, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rt = round(r * 2 / sqrt3, 2); var r2 = round(r / 2, 2); - var rs = round(r, 2); + var rs = round(rx, 2); return align(angle, standoff, 'M-' + r2 + ',-' + rt + 'V' + rt + 'L' + rs + ',0Z'); } }, 'triangle-ne': { n: 9, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var r1 = round(r * 0.6, 2); @@ -132,7 +133,7 @@ module.exports = { }, 'triangle-se': { n: 10, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var r1 = round(r * 0.6, 2); @@ -142,7 +143,7 @@ module.exports = { }, 'triangle-sw': { n: 11, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var r1 = round(r * 0.6, 2); @@ -152,7 +153,7 @@ module.exports = { }, 'triangle-nw': { n: 12, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var r1 = round(r * 0.6, 2); @@ -162,12 +163,12 @@ module.exports = { }, pentagon: { n: 13, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var x1 = round(r * 0.951, 2); var x2 = round(r * 0.588, 2); - var y0 = round(-r, 2); + var y0 = round(-rx, ry, 2); var y1 = round(r * -0.309, 2); var y2 = round(r * 0.809, 2); return align(angle, standoff, 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2 + 'H-' + x2 + @@ -176,10 +177,10 @@ module.exports = { }, hexagon: { n: 14, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; - var y0 = round(r, 2); + var y0 = round(rx, 2); var y1 = round(r / 2, 2); var x = round(r * sqrt3 / 2, 2); return align(angle, standoff, 'M' + x + ',-' + y1 + 'V' + y1 + 'L0,' + y0 + @@ -188,10 +189,10 @@ module.exports = { }, hexagon2: { n: 15, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; - var x0 = round(r, 2); + var x0 = round(rx, 2); var x1 = round(r / 2, 2); var y = round(r * sqrt3 / 2, 2); return align(angle, standoff, 'M-' + x1 + ',' + y + 'H' + x1 + 'L' + x0 + @@ -200,7 +201,7 @@ module.exports = { }, octagon: { n: 16, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var a = round(r * 0.924, 2); @@ -211,7 +212,7 @@ module.exports = { }, star: { n: 17, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rs = r * 1.4; @@ -232,7 +233,7 @@ module.exports = { }, hexagram: { n: 18, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var y = round(r * 0.66, 2); @@ -246,7 +247,7 @@ module.exports = { }, 'star-triangle-up': { n: 19, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var x = round(r * sqrt3 * 0.8, 2); @@ -260,7 +261,7 @@ module.exports = { }, 'star-triangle-down': { n: 20, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var x = round(r * sqrt3 * 0.8, 2); @@ -274,7 +275,7 @@ module.exports = { }, 'star-square': { n: 21, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rp = round(r * 1.1, 2); @@ -287,7 +288,7 @@ module.exports = { }, 'star-diamond': { n: 22, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rp = round(r * 1.4, 2); @@ -300,7 +301,7 @@ module.exports = { }, 'diamond-tall': { n: 23, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var x = round(r * 0.7, 2); @@ -310,7 +311,7 @@ module.exports = { }, 'diamond-wide': { n: 24, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var x = round(r * 1.4, 2); @@ -320,30 +321,30 @@ module.exports = { }, hourglass: { n: 25, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; - var rs = round(r, 2); + var rs = round(rx, 2); return align(angle, standoff, 'M' + rs + ',' + rs + 'H-' + rs + 'L' + rs + ',-' + rs + 'H-' + rs + 'Z'); }, noDot: true }, bowtie: { n: 26, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; - var rs = round(r, 2); + var rs = round(rx, 2); return align(angle, standoff, 'M' + rs + ',' + rs + 'V-' + rs + 'L-' + rs + ',' + rs + 'V-' + rs + 'Z'); }, noDot: true }, 'circle-cross': { n: 27, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; - var rs = round(r, 2); + var rs = round(rx, 2); return align(angle, standoff, 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs + 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs + 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z'); @@ -353,10 +354,10 @@ module.exports = { }, 'circle-x': { n: 28, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; - var rs = round(r, 2); + var rs = round(rx, 2); var rc = round(r / sqrt2, 2); return align(angle, standoff, 'M' + rc + ',' + rc + 'L-' + rc + ',-' + rc + 'M' + rc + ',-' + rc + 'L-' + rc + ',' + rc + @@ -368,10 +369,10 @@ module.exports = { }, 'square-cross': { n: 29, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; - var rs = round(r, 2); + var rs = round(rx, 2); return align(angle, standoff, 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs + 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z'); }, @@ -380,10 +381,10 @@ module.exports = { }, 'square-x': { n: 30, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; - var rs = round(r, 2); + var rs = round(rx, 2); return align(angle, standoff, 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs + 'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs + 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z'); @@ -393,7 +394,7 @@ module.exports = { }, 'diamond-cross': { n: 31, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rd = round(r * 1.3, 2); @@ -405,7 +406,7 @@ module.exports = { }, 'diamond-x': { n: 32, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rd = round(r * 1.3, 2); @@ -419,7 +420,7 @@ module.exports = { }, 'cross-thin': { n: 33, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rc = round(r * 1.4, 2); @@ -431,7 +432,7 @@ module.exports = { }, 'x-thin': { n: 34, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rx = round(r, 2); @@ -444,7 +445,7 @@ module.exports = { }, asterisk: { n: 35, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rc = round(r * 1.2, 2); @@ -459,11 +460,11 @@ module.exports = { }, hash: { n: 36, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var r1 = round(r / 2, 2); - var r2 = round(r, 2); + var r2 = round(rx, 2); return align(angle, standoff, 'M' + r1 + ',' + r2 + 'V-' + r2 + 'M' + (r1 - r2) + ',-' + r2 + 'V' + r2 + @@ -475,7 +476,7 @@ module.exports = { }, 'y-up': { n: 37, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var x = round(r * 1.2, 2); @@ -489,7 +490,7 @@ module.exports = { }, 'y-down': { n: 38, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var x = round(r * 1.2, 2); @@ -503,7 +504,7 @@ module.exports = { }, 'y-left': { n: 39, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var y = round(r * 1.2, 2); @@ -517,7 +518,7 @@ module.exports = { }, 'y-right': { n: 40, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var y = round(r * 1.2, 2); @@ -531,7 +532,7 @@ module.exports = { }, 'line-ew': { n: 41, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rc = round(r * 1.4, 2); @@ -543,7 +544,7 @@ module.exports = { }, 'line-ns': { n: 42, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rc = round(r * 1.4, 2); @@ -555,10 +556,10 @@ module.exports = { }, 'line-ne': { n: 43, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; - var rx = round(r, 2); + var rx = round(rx, 2); return align(angle, standoff, 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx); }, needLine: true, @@ -567,10 +568,10 @@ module.exports = { }, 'line-nw': { n: 44, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; - var rx = round(r, 2); + var rx = round(rx, 2); return align(angle, standoff, 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx); }, needLine: true, @@ -579,10 +580,10 @@ module.exports = { }, 'arrow-up': { n: 45, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; - var rx = round(r, 2); + var rx = round(rx, 2); var ry = round(r * 2, 2); return align(angle, standoff, 'M0,0L-' + rx + ',' + ry + 'H' + rx + 'Z'); }, @@ -591,10 +592,10 @@ module.exports = { }, 'arrow-down': { n: 46, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; - var rx = round(r, 2); + var rx = round(rx, 2); var ry = round(r * 2, 2); return align(angle, standoff, 'M0,0L-' + rx + ',-' + ry + 'H' + rx + 'Z'); }, @@ -602,32 +603,32 @@ module.exports = { }, 'arrow-left': { n: 47, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rx = round(r * 2, 2); - var ry = round(r, 2); + var ry = round(rx, 2); return align(angle, standoff, 'M0,0L' + rx + ',-' + ry + 'V' + ry + 'Z'); }, noDot: true }, 'arrow-right': { n: 48, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rx = round(r * 2, 2); - var ry = round(r, 2); + var ry = round(rx, 2); return align(angle, standoff, 'M0,0L-' + rx + ',-' + ry + 'V' + ry + 'Z'); }, noDot: true }, 'arrow-bar-up': { n: 49, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; - var rx = round(r, 2); + var rx = round(rx, 2); var ry = round(r * 2, 2); return align(angle, standoff, 'M-' + rx + ',0H' + rx + 'M0,0L-' + rx + ',' + ry + 'H' + rx + 'Z'); }, @@ -637,10 +638,10 @@ module.exports = { }, 'arrow-bar-down': { n: 50, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; - var rx = round(r, 2); + var rx = round(rx, 2); var ry = round(r * 2, 2); return align(angle, standoff, 'M-' + rx + ',0H' + rx + 'M0,0L-' + rx + ',-' + ry + 'H' + rx + 'Z'); }, @@ -649,11 +650,11 @@ module.exports = { }, 'arrow-bar-left': { n: 51, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rx = round(r * 2, 2); - var ry = round(r, 2); + var ry = round(rx, 2); return align(angle, standoff, 'M0,-' + ry + 'V' + ry + 'M0,0L' + rx + ',-' + ry + 'V' + ry + 'Z'); }, needLine: true, @@ -661,11 +662,11 @@ module.exports = { }, 'arrow-bar-right': { n: 52, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var rx = round(r * 2, 2); - var ry = round(r, 2); + var ry = round(rx, 2); return align(angle, standoff, 'M0,-' + ry + 'V' + ry + 'M0,0L-' + rx + ',-' + ry + 'V' + ry + 'Z'); }, needLine: true, @@ -673,7 +674,7 @@ module.exports = { }, arrow: { n: 53, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var headAngle = PI / 2.5; // 36 degrees - golden ratio @@ -692,7 +693,7 @@ module.exports = { }, 'arrow-wide': { n: 54, - f: function(r, angle, standoff) { + f: function(r, ry, angle, standoff) { if(skipAngle(angle)) return emptyPath; var headAngle = PI / 4; // 90 degrees diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js index 84844d8fc60..8bef7235cfc 100644 --- a/src/traces/scatter/attributes.js +++ b/src/traces/scatter/attributes.js @@ -77,6 +77,14 @@ module.exports = { 'where `x0` is the starting coordinate and `dx` the step.' ].join(' ') }, + x1: { + valType: 'any', + arrayOk: true, + dflt: 0, + editType: 'calc+clearAxisTypes', + anim: true, + description: 'Sets the x1 range coordinates.' + }, dx: { valType: 'number', dflt: 1, @@ -94,8 +102,7 @@ module.exports = { description: 'Sets the y coordinates.' }, y0: { - valType: 'any', - dflt: 0, + valType: 'data_array', editType: 'calc+clearAxisTypes', anim: true, description: [ @@ -105,6 +112,14 @@ module.exports = { 'where `y0` is the starting coordinate and `dy` the step.' ].join(' ') }, + y1: { + valType: 'any', + arrayOk: true, + dflt: 0, + editType: 'calc+clearAxisTypes', + anim: true, + description: 'Sets the y1 range coordinates.' + }, dy: { valType: 'number', dflt: 1, diff --git a/src/traces/scatter/calc.js b/src/traces/scatter/calc.js index 619d3b83a44..d79c6a243fa 100644 --- a/src/traces/scatter/calc.js +++ b/src/traces/scatter/calc.js @@ -23,6 +23,13 @@ function calc(gd, trace) { var x = xObj.vals; var y = yObj.vals; + var origX1 = xa.makeCalcdata(trace, 'x1'); + var origY1 = ya.makeCalcdata(trace, 'y1'); + var x1Obj = alignPeriod(trace, xa, 'x1', origX1); + var y1Obj = alignPeriod(trace, ya, 'y1', origY1); + var x1 = x1Obj.vals; + var y1 = y1Obj.vals; + var serieslen = trace._length; var cd = new Array(serieslen); var ids = trace.ids; @@ -50,7 +57,7 @@ function calc(gd, trace) { interpolate = stackGroupOpts.stackgaps === 'interpolate'; } else { var ppad = calcMarkerSize(trace, serieslen); - calcAxisExpansion(gd, trace, xa, ya, x, y, ppad); + calcAxisExpansion(gd, trace, xa, ya, x, y, x1, y1, ppad); } var hasPeriodX = !!trace.xperiodalignment; @@ -90,6 +97,14 @@ function calc(gd, trace) { cdi[xAttr] = cdi[yAttr] = BADNUM; } + + var x1Valid = isNumeric(x[i]); + var y1Valid = isNumeric(y[i]); + if (x1Valid && y1Valid) { + cdi['x1'] = x1[i]; + cdi['y1'] = y1[i]; + } + if(ids) { cdi.id = String(ids[i]); } @@ -156,7 +171,7 @@ function calc(gd, trace) { return cd; } -function calcAxisExpansion(gd, trace, xa, ya, x, y, ppad) { +function calcAxisExpansion(gd, trace, xa, ya, x, y, x1, y1, ppad) { var serieslen = trace._length; var fullLayout = gd._fullLayout; var xId = xa._id; @@ -216,8 +231,12 @@ function calcAxisExpansion(gd, trace, xa, ya, x, y, ppad) { } // N.B. asymmetric splom traces call this with blank {} xa or ya - if(xId) trace._extremes[xId] = Axes.findExtremes(xa, x, xOptions); - if(yId) trace._extremes[yId] = Axes.findExtremes(ya, y, yOptions); + if(xId) { + trace._extremes[xId] = Axes.findExtremes(xa, Array.isArray(x1) ? x.concat(x1) : x, xOptions) + } + if(yId) { + trace._extremes[yId] = Axes.findExtremes(ya, Array.isArray(y1) ? y.concat(y1) : y, yOptions); + } } function calcMarkerSize(trace, serieslen) { diff --git a/src/traces/scatter/hover.js b/src/traces/scatter/hover.js index 04b0de04e17..e6924ef771c 100644 --- a/src/traces/scatter/hover.js +++ b/src/traces/scatter/hover.js @@ -61,16 +61,35 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { return (dyRaw < rad) ? (kink * dyRaw / rad) : (dyRaw - rad + kink); }; - // scatter points: d.mrc is the calculated marker radius - // adjust the distance so if you're inside the marker it - // always will show up regardless of point size, but - // prioritize smaller points var dxy = function(di) { - var rad = Math.max(minRad, di.mrc || 0); - var dx = xa.c2p(di.x) - xpx; - var dy = ya.c2p(di.y) - ypx; - return Math.max(Math.sqrt(dx * dx + dy * dy) - rad, 1 - minRad / rad); - }; + // scatter points: d.mrxc and d.mryc are the calculated marker radius + // adjust the distance so if you're inside the marker it + // always will show up regardless of point size, but + // prioritize smaller points + if (di.x1 == null && di.y1 == null) { + var rad = Math.max(minRad, di.mrxc || 0, di.mryc || 0); + var dx = xa.c2p(di.x) - xpx; + var dy = ya.c2p(di.y) - ypx; + return Math.max(Math.sqrt(dx * dx + dy * dy) - rad, 1 - minRad / rad); + } + + var radX = Math.max(minRad, di.mrxc || 0); + var radY = Math.max(minRad, di.mryc || 0); + + var x = di.xc || di.x; + var y = di.yc || di.y; + + var dx = x - xpx; + var dy = y - ypx; + + var d = ((dx * dx) / (radX * radX)) + ((dy * dy) / (radY * radY)); + + if (d <= 1) { + return 0; + } else { + return Infinity; + } + }; var distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy); Fx.getClosest(cd, distfn, pointData); diff --git a/src/traces/scatter/plot.js b/src/traces/scatter/plot.js index 3ab3481b7b5..3a6971143ff 100644 --- a/src/traces/scatter/plot.js +++ b/src/traces/scatter/plot.js @@ -518,9 +518,14 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition var enter = join.enter().append('path') .classed('point', true); + var styleFns; + if(showMarkers) { + styleFns = Drawing.makePointStyleFns(trace, xa, ya); + } + if(hasTransition) { enter - .call(Drawing.pointStyle, trace, gd) + .call(Drawing.pointStyle, trace, xa, ya, styleFns, gd) .call(Drawing.translatePoints, xa, ya) .style('opacity', 0) .transition() @@ -529,11 +534,6 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition join.order(); - var styleFns; - if(showMarkers) { - styleFns = Drawing.makePointStyleFns(trace); - } - join.each(function(d) { var el = d3.select(this); var sel = transition(el); diff --git a/src/traces/scatter/style.js b/src/traces/scatter/style.js index 734ecf9f9d0..6a126ddad21 100644 --- a/src/traces/scatter/style.js +++ b/src/traces/scatter/style.js @@ -11,10 +11,13 @@ function style(gd) { return d[0].trace.opacity; }); + var xa = gd._fullLayout.xaxis; + var ya = gd._fullLayout.yaxis; + s.selectAll('g.points').each(function(d) { var sel = d3.select(this); var trace = d.trace || d[0].trace; - stylePoints(sel, trace, gd); + stylePoints(sel, trace, xa, ya, gd); }); s.selectAll('g.text').each(function(d) { @@ -32,22 +35,22 @@ function style(gd) { Registry.getComponentMethod('errorbars', 'style')(s); } -function stylePoints(sel, trace, gd) { - Drawing.pointStyle(sel.selectAll('path.point'), trace, gd); +function stylePoints(sel, trace, xa, ya, gd) { + Drawing.pointStyle(sel.selectAll('path.point'), trace, xa, ya, gd); } function styleText(sel, trace, gd) { Drawing.textPointStyle(sel.selectAll('text'), trace, gd); } -function styleOnSelect(gd, cd, sel) { +function styleOnSelect(gd, cd, sel, xa, ya) { var trace = cd[0].trace; if(trace.selectedpoints) { Drawing.selectedPointStyle(sel.selectAll('path.point'), trace); Drawing.selectedTextStyle(sel.selectAll('text'), trace); } else { - stylePoints(sel, trace, gd); + stylePoints(sel, trace, xa, ya, gd); styleText(sel, trace, gd); } } diff --git a/src/traces/scatter/xy_defaults.js b/src/traces/scatter/xy_defaults.js index 2c9eb19fa51..f184db55c2a 100644 --- a/src/traces/scatter/xy_defaults.js +++ b/src/traces/scatter/xy_defaults.js @@ -28,6 +28,9 @@ module.exports = function handleXYDefaults(traceIn, traceOut, layout, coerce) { coerce('dx'); } + coerce('x1'); + coerce('y1'); + traceOut._length = len; return len;